HDU 3820 Golden Eggs( 最小割 奇特建图)经典

来源:互联网 发布:fc2 最新域名设置 编辑:程序博客网 时间:2024/05/04 23:07

Golden Eggs

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 501    Accepted Submission(s): 281


Problem Description
There is a grid with N rows and M columns. In each cell you can choose to put a golden or silver egg in it, or just leave it empty. If you put an egg in the cell, you will get some points which depends on the color of the egg. But for every pair of adjacent eggs with the same color, you lose G points if there are golden and lose S points otherwise. Two eggs are adjacent if and only if there are in the two cells which share an edge. Try to make your points as high as possible.
 

Input
The first line contains an integer T indicating the number of test cases.
There are four integers N, M, G and S in the first line of each test case. Then 2*N lines follows, each line contains M integers. The j-th integer of the i-th line Aij indicates the points you will get if there is a golden egg in the cell(i,j). The j-th integer of the (i+N)-th line Bij indicates the points you will get if there is a silver egg in the cell(i,j).

Technical Specification
1. 1 <= T <= 20
2. 1 <= N,M <= 50
3. 1 <= G,S <= 10000
4. 1 <= Aij,Bij <= 10000
 

Output
For each test case, output the case number first and then output the highest points in a line.
 

Sample Input
22 2 100 1001 15 11 41 11 4 85 95100 100 10 1010 10 100 100
 

Sample Output
Case 1: 9Case 2: 225
 

Author
hanshuai
 

Source
The 6th Central China Invitational Programming Contest and 9th Wuhan University Programming Contest Final
题意:有一个n*m的格子,可以放两种颜色我蛋:金蛋和银蛋。如果相邻格子里放同一颜色的蛋:如果为金蛋则减少价值为G,如果银蛋则减少价值为S。接下来n行m列表示放金蛋得到的价值mp1[][],再接下来n行m列表示放银蛋得到的价值mp2[][]。问最大能得到的价值。
解题:vs:源点,vt:汇点。建图:把n*m个格子分成奇偶两部分,每个点都需要拆成两个点v,v'。奇点v:<vs , v , mp1[i][j]>,<v , v' , INF>,<v' , vt , mp2[i][j]>。与v相邻点u,<v , u' , G>。偶点u:<vs , u , mp2[i][j]>,<u , u' , INF>,<u' , vt , mp1[i][j]>。与u相邻点v,<u , v' , S>。这样图建好了,那么答案:mp1[][]+mp2[][] -maxflow。
/*最大流:SAP算法,与ISAP的差别就是不用预处理*/#include<stdio.h>#include<string.h>#include<queue>#include<algorithm>using namespace std;#define captype intconst int MAXN = 100010;   //点的总数const int MAXM = 400010;    //边的总数const int INF = 1<<30;struct EDG{    int to,next;    captype cap,flow;} edg[MAXM];int eid,head[MAXN];int gap[MAXN];  //每种距离(或可认为是高度)点的个数int dis[MAXN];  //每个点到终点eNode 的最短距离int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边int pre[MAXN];void init(){    eid=0;    memset(head,-1,sizeof(head));}//有向边 三个参数,无向边4个参数void addEdg(int u,int v,captype c,captype rc=0){    edg[eid].to=v; edg[eid].next=head[u];    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;    edg[eid].to=u; edg[eid].next=head[v];    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;}captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源点和汇点的总点个数,这个一定要注意    memset(gap,0,sizeof(gap));    memset(dis,0,sizeof(dis));    memcpy(cur,head,sizeof(head));    pre[sNode] = -1;    gap[0]=n;    captype ans=0;  //最大流    int u=sNode;    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点        if(u==eNode){   //找到一条可增流的路            captype Min=INF ;            int inser;            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min            if(Min>edg[i].cap-edg[i].flow){                Min=edg[i].cap-edg[i].flow;                inser=i;            }            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){                edg[i].flow+=Min;                edg[i^1].flow-=Min;  //可回流的边的流量            }            ans+=Min;            u=edg[inser^1].to;            continue;        }        bool flag = false;  //判断能否从u点出发可往相邻点流        int v;        for(int i=cur[u]; i!=-1; i=edg[i].next){            v=edg[i].to;            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){                flag=true;                cur[u]=pre[v]=i;                break;            }        }        if(flag){            u=v;            continue;        }        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1        int Mind= n;        for(int i=head[u]; i!=-1; i=edg[i].next)        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){            Mind=dis[edg[i].to];            cur[u]=i;        }        gap[dis[u]]--;        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1        gap[dis[u]]++;        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边    }    return ans;}int main(){    int T,n,m,vs,vt,mp1[55][55],mp2[55][55],G,S;    int dir[4][2]={0,1,0,-1,1,0,-1,0};    scanf("%d",&T);    for(int _case=1; _case<=T; ++_case){        int ans=0;        scanf("%d%d%d%d",&n,&m,&G,&S);        for(int i=0; i<n; i++)            for(int j=0; j<m; j++)              scanf("%d",&mp1[i][j]) , ans+=mp1[i][j];        for(int i=0; i<n; i++)            for(int j=0; j<m; j++)              scanf("%d",&mp2[i][j]) , ans+=mp2[i][j];        vs = 2*n*m; vt = vs+1;        init();        for(int i=0; i<n; i++)            for(int j=0; j<m; j++){                int u=i*m+j;                  if((i+j)&1){                     addEdg(vs , u , mp1[i][j]);                     addEdg(u , u+n*m , INF);                     addEdg(u+n*m , vt , mp2[i][j]);                     for(int e=0; e<4; e++)                     {                         int ti , tj;                         ti=i+dir[e][0];                         tj=j+dir[e][1];                         if(ti>=0&&ti<n&&tj>=0&&tj<m)                            addEdg(u , ti*m+tj+n*m , G);                     }                  }                  else{                    addEdg(vs , u , mp2[i][j]);                     addEdg(u , u+n*m , INF);                     addEdg(u+n*m , vt , mp1[i][j]);                     for(int e=0; e<4; e++)                     {                         int ti , tj;                         ti=i+dir[e][0];                         tj=j+dir[e][1];                         if(ti>=0&&ti<n&&tj>=0&&tj<m)                            addEdg(u , ti*m+tj+n*m , S);                     }                  }            }        ans-=maxFlow_sap(vs , vt , vt+1);        printf("Case %d: %d\n",_case , ans);    }}


0 0