• 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
    ",cost);
        return 0;
    }
    
  • 相关阅读:
    MySQL 列子查询及 IN、ANY、SOME 和 ALL 操作符的使用
    MySQL 标量子查询
    MySQL 子查询(subquery)语法与用法实例
    如何上传本地音乐获取MP3外链(欢迎分享和转载)
    RabbitMQ与Kafka的区别及其简单原理实现
    MySQL中varchar和char的区别
    MySQL降低insert, update, delete的优先级来优化性能
    Eclipse代码自动提示
    Java生成XML文件
    Java读取XML文件
  • 原文地址:https://www.cnblogs.com/lishiyao/p/7536949.html
Copyright © 2020-2023  润新知