• 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;
    }
  • 相关阅读:
    后序非递归遍历二叉树的应用
    关于驰骋工作流程引擎,工作流程管理系统演示与学习环境发布的通知。
    驰骋工作流程引擎,ccflow,如何把子线程的数据汇总到合流节点表单中去?
    关于取消ccflow abc 级别用户与开放表单设计器源代码的通知
    驰骋工作流程引擎问题解答,武汉朋友。
    ccform 单据打印的规则调整与新增功能发布说明
    发几个傻瓜表单设计器预览图片,以方便大家学习.
    关于工作流程管理系统中的现有版本自由表单设计器的停止升级与新版本将要发布的声明.
    ccflow向流程开始节点表单传输数据方法大全
    利用开源的驰骋工作流程引擎,处理的集团公司流程应用案例之一.
  • 原文地址:https://www.cnblogs.com/zufezzt/p/5393392.html
Copyright © 2020-2023  润新知