BZOJ 3171 循环格(费用流)

题意

一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子。每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0)。给定一个起始位置(r,c),你可以沿着箭头防线在格子间行走。即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1);如果是上箭头那么走到(r-1,c);如果是下箭头那么走到(r+1,c);每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。
一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。给定一个循环格,你需要计算最少需要修改多少个元素使其完美。

思路

显然满足题中条件的图需要满足每个点的出度和入度都为1。
考虑费用流,把每个点\((i,j)\)拆成两个点\((i_1,j_1)\)\((i_2,j_2)\)
因为一个点只能有一个出度,所以\(S-(i_1,j_1)\)的容量应该为1,
同理\((i_2,j_2)-T\)的容量也应该为1,\((i_1,j_1)\)\((i_2,j_2)\)之间互相连边,
原图中的边对应与新图的边费用应该为0,其余三个方向的边费用设为1.
那么跑一遍minCostmaxFlow即可得出答案。

代码

# include<bits/stdc++.h>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 100000007
# define INF 1e16
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(register int i=a; i<=n; ++i)
# define FDR(i,a,n) for(register int i=a; i>=n; --i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline char nc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int Scan(){
    char ch=nc();int sum=0, f=1;
    if (ch=='-') f=-1, ch=nc();
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*f;
}
const int N=250;
//Code begin....

int n, m, s, t;
char G[20][20];
struct Edge{
    int to, next, cap, flow, cost;
}edge[N*20];
int head[N<<1], tol, pre[N<<1], dis[N<<1], nn;
bool vis[N<<1];
int ps[4][2]={1,0,-1,0,0,1,0,-1};

int ID(int x, int y){return x*m+y;}
void init(int n){nn=n; tol=0; mem(head,-1);}
void add_edge(int u, int v, int cap, int cost){
    edge[tol]=Edge{v,head[u],cap,0,cost}; head[u]=tol++;
    edge[tol]=Edge{u,head[v],0,0,-cost}; head[v]=tol++;
}
bool spfa(int s, int t){
    queue<int>q;
    FOR(i,0,nn-1) dis[i]=INF, vis[i]=false, pre[i]=-1;
    dis[s]=0; vis[s]=true; q.push(s);
    while (!q.empty()) {
        int u=q.front(); q.pop();
        vis[u]=false;
        for (int i=head[u]; i!=-1; i=edge[i].next) {
            int v=edge[i].to;
            if (edge[i].cap>edge[i].flow && dis[v]>dis[u]+edge[i].cost) {
                dis[v]=dis[u]+edge[i].cost;
                pre[v]=i;
                if (!vis[v]) vis[v]=true, q.push(v);
            }
        }
    }
    if (pre[t]==-1) return false;
    else return true;
}
int minCostmaxFlow(int s, int t, int &cost){
    int flow=0;
    cost=0;
    while (spfa(s,t)) {
        int Min=INF;
        for (int i=pre[t]; i!=-1; i=pre[edge[i^1].to]) {
            if (Min>edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow;
        }
        for (int i=pre[t]; i!=-1; i=pre[edge[i^1].to]) {
            edge[i].flow+=Min; edge[i^1].flow-=Min;
            cost+=edge[i].cost*Min;
        }
        flow+=Min;
    }
    return flow;
}
int main ()
{
    scanf("%d%d",&n,&m);
    s=2*n*m; t=2*n*m+1;
    init(t+1);
    FOR(i,0,n-1) scanf("%s",G[i]);
    FOR(i,0,n-1) FOR(j,0,m-1) {
        FOR(k,0,3) {
            int dx=(i+ps[k][0]+n)%n, dy=(j+ps[k][1]+m)%m;
            if ((k==0&&G[i][j]=='D')||(k==1&&G[i][j]=='U')||(k==2&&G[i][j]=='R')||(k==3&&G[i][j]=='L')) add_edge(ID(i,j),ID(dx,dy)+n*m,1,0);
            else add_edge(ID(i,j),ID(dx,dy)+n*m,1,1);
        }
        add_edge(s,ID(i,j),1,0), add_edge(ID(i,j)+n*m,t,1,0);
    }
    int cost;
    minCostmaxFlow(s,t,cost);
    printf("%d\n",cost);
    return 0;
}
posted @ 2017-09-17 20:23  free-loop  阅读(162)  评论(0编辑  收藏  举报