zoukankan      html  css  js  c++  java
  • FZU 2143 Board Game

    这题好难。。第一次遇到这样的建图,表示是看了好多题解才懂的。

    首先这是一个费用流的题,但是请注意,并不是达到最大流时候的最小费用

    首先分析每一个位置对答案做出的贡献,即a[i][j]*a[i][j]-2*a[i][j]*b[i][j]+b[i][j]*b[i][j],最后一项是个常数,所以我们只要计算前两项的总和的最小值。

    a[i][j]表示的意义是对i,j这个位置的操作次数

    假设操作了p次,这个点对答案做出的贡献是p*p-2*p*b[i][j]+b[i][j]*b[i][j]

    假设操作了p-1次,这个点对答案做出的贡献是(p-1)*(p-1)-2*(p-1)*b[i][j]+b[i][j]*b[i][j]

    第p次的贡献与第p-1次的贡献的差值是2*p-1-2*b[i][j]。

    建图:

    首先对矩阵间隔染上黑白两色,我们只对黑色格子进行操作,与黑色格子相邻的白色格子可以连动。

    原点到每一个黑色格子连k条边,容量为1,第p条的费用是2*p-1-2*b[i][j],表示进行了第p次操作,在进行了p-1次操作的基础上,对答案的贡献新增加了2*p-1-2*b[i][j]

    每一个白格子到汇点连k条边,容量为1,第p条的费用是2*p-1-2*b[i][j],意义同上。

    请注意:2*p-1-2*b[i][j]这个式子是随着p增大而增大的,所以流量必然是从费用小的开始流,恰好与题目操作吻合。

    黑色格子与他相邻的白格子连变,容量为INF,费用为0,这样能保证一个黑格子有流量流过的时候必然会把流量流向一个相邻白格子,与题目操作吻合。

    接下里就是做一次费用流,但是请注意:

    费用流算法结束的标志并不是SPFA的时候不能达到汇点!

    而是当达到汇点的距离>=0的时候就结束了,这个时候答案已经最小了。

    再继续流下去,答案会变大而不是减小。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<algorithm>
    using namespace std;
    
    const int maxn=200;
    int n,m,k;
    int b[15][15];
    int id[15][15];
    
    const int INF=0x7FFFFFFF;
    struct Edge
    {
        int from,to,cap,flow,cost;
    };
    int len,s,t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];
    int use[maxn];
    int ans;
    int dir[4][2]=
    {
        {1,0},
        {-1,0},
        {0,-1},
        {0,1},
    };
    
    void init()
    {
        for(int i=0; i<maxn; i++) G[i].clear();
        edges.clear();
    }
    
    void Addedge(int from,int to,int cap,int cost)
    {
        edges.push_back((Edge)
        {
            from,to,cap,0,cost
        });
        edges.push_back((Edge)
        {
            to,from,0,0,-cost
        });
        len=edges.size();
        G[from].push_back(len-2);
        G[to].push_back(len-1);
    }
    
    bool BellmanFord(int s,int t,int &flow,int &cost)
    {
    
        for(int i=0; i<maxn; i++) d[i]=INF;
    
        memset(inq,0,sizeof(inq));
        memset(p,-1,sizeof(p));
    
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;
    
        queue<int>Q;
        Q.push(s);
        while(!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            inq[u]=0;
            for(int i=0; i<G[u].size(); i++)
            {
                Edge& e=edges[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost)
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to])
                    {
                        Q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]>=0) return false;
        flow+=a[t];
        cost+=d[t]*a[t];
        int u=t;
        while(u!=s)
        {
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            u=edges[p[u]].from;
        }
        return true;
    }
    
    void Mincost (int s,int t)
    {
        int flow=0,cost=0;
        while(BellmanFord(s,t,flow,cost));
        ans=ans+cost;
    }
    
    bool P(int a,int b)
    {
        if(a>=1&&a<=n&&b>=1&&b<=m) return 1;
        return 0;
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        for(int cas=1; cas<=T; cas++)
        {
            init();
            scanf("%d%d%d",&n,&m,&k);
            ans=0;
            int ID=1;
            for(int i=1; i<=n; i++)
                for(int j=1; j<=m; j++)
                {
                    scanf("%d",&b[i][j]);
                    ans=ans+b[i][j]*b[i][j];
                    id[i][j]=ID++;
                }
    
            s=0;
            t=n*m+1;
            for(int i=1; i<=n; i++)
            {
                for(int j=1; j<=m; j++)
                {
                    if((i+j)%2==1)
                    {
                        for(int p=1; p<=k; p++)
                            Addedge(s,id[i][j],1,2*p-1-2*b[i][j]);
    
                        for(int d=0; d<4; d++)
                        {
                            int x=i+dir[d][0],y=j+dir[d][1];
                            if(P(x,y)) Addedge(id[i][j],id[x][y],INF,0);
                        }
                    }
                    else
                    {
                        for(int p=1; p<=k; p++)
                            Addedge(id[i][j],t,1,2*p-1-2*b[i][j]);
                    }
                }
            }
            Mincost(s,t);
            printf("Case %d: %d
    ",cas,ans);
        }
        return 0;
    }
  • 相关阅读:
    用代理IP进行简单的爬虫——爬高匿代理网站
    python利用django实现简单的登录和注册,并利用session实现了链接数据库
    python基础知识——基于python3.6
    笔记2_列表、元组、字典
    wpf Command
    可枚举对象操作
    2019寒假训练营寒假作业(二) 程序题部分
    2019-01-23 寒假作业(一)
    2019寒假训练营第二次作业
    网络空间安全概论 学习笔记(二)
  • 原文地址:https://www.cnblogs.com/zufezzt/p/5393392.html
Copyright © 2011-2022 走看看