【TJOI2013】循环格

费用流

题目大意:

给定一个 $n\times m$ 的图,其中第一行和最后一行,第一列和最后一列视为相邻(例如,$3\times 5$ 的图,$(1,5)$ 的上面为 $(3,5)$,右边为 $(1,1)$)。

每个点有箭头,指向上、下、左、右四个方向之一。

现在要改变尽量少的箭头的方向,使得所有箭头构成若干回路。

求最少改变多少箭头的方向。

题解:

一个回路满足每个点入度、出度均为 $1$。

图上已经保证每个点出度为 $1$ 了,那么我们再保证每个点入度为 $1$ 即可。

那么就是在限制每个点的入度的基础上,让花费最小。

考虑建图跑费用流。

对每个点建一个入点和出点,入点向其箭头原来指的方向的出点连容量 $1$,费用 $0$ 的边,向其他方向的出点连容量 $1$,费用 $1$ 的边,表示改变或不改变这个箭头的方向分别要带来的费用。

然后源点向每个入点,每个出点向汇点连容量 $1$,费用 $0$ 的边,表示限制每个点的入度、出度均为 $1$。

满流下的最小费用即为答案。

Code:

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=20,M=5005;
int id[N][N],n,m,nod=0,head[M],cnt=1,dis[M],T,iter[M];
bool vis[M];
struct edge{
    int to,nxt,cap,cost;
}e[M<<1];
inline void addedge(int u,int v,int cap,int cost){
    e[++cnt]=(edge){v,head[u],cap,cost},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],0,-cost},head[v]=cnt;
}
queue<int>q;
int dfs(int u,int f){
    if(!f||u==T)return f;
    vis[u]=1;
    int ret=0;
    for(int&i=iter[u];i;i=e[i].nxt)
        if(e[i].cap&&!vis[e[i].to]&&dis[e[i].to]==dis[u]+e[i].cost){
            int d=dfs(e[i].to,min(e[i].cap,f));
            e[i].cap-=d,e[i^1].cap+=d,ret+=d,f-=d;
            if(!f)break;
        }
    vis[u]=0;
    return ret;
}
bool MCMF(int&cost){
    memset(dis,0x3f,(T+2)<<2);
    dis[0]=0;
    for(q.push(0);!q.empty();){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
            if(e[i].cap&&dis[e[i].to]>dis[u]+e[i].cost){
                dis[e[i].to]=dis[u]+e[i].cost;
                if(!vis[e[i].to])vis[e[i].to]=1,q.push(e[i].to);
            }
    }
    if(dis[T]>1e9)return 0;
    memcpy(iter,head,(T+2)<<2);
    cost+=dis[T]*dfs(0,0x3fffffff);
    return 1;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
            id[i][j]=++nod;
    for(int i=0;i<n;++i){
        static char s[20];
        cin>>s;
        for(int j=0;j<m;++j){
            const char c=s[j];
            ++nod;
            addedge(0,nod,1,0);
            addedge(nod,id[(i-1+n)%n][j],1,c=='U'?0:1);
            addedge(nod,id[(i+1)%n][j],1,c=='D'?0:1);
            addedge(nod,id[i][(j-1+m)%m],1,c=='L'?0:1);
            addedge(nod,id[i][(j+1)%m],1,c=='R'?0:1);
        }
    }
    T=++nod;
    for(int i=0;i<n;++i)for(int j=0;j<m;++j)addedge(id[i][j],T,1,0);
    int cost=0;
    while(MCMF(cost));
    cout<<cost<<'\n';
    return 0;
}