Golden Eggs
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 367 Accepted Submission(s): 208
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
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
2 2 2 100 100 1 1 5 1 1 4 1 1 1 4 85 95 100 100 10 10 10 10 100 100
Sample Output
Case 1: 9 Case 2: 225
大致题意:
给出一块n*m的区域,已知区域中的每个格子都可以放金蛋或者是银弹,已知每个格子放金蛋或者是放银弹时可以得到的收益,分别用矩阵map1[][],map2[][]表示。如果相邻的两个格子放的蛋相同的话会扣去一定的收益,金蛋的话会扣去g,银弹s。求收益的最大值是多少。
大致思路:
首先,对于某个确定的位置,只能选择金蛋或者银弹的其中一个放上去,从这里可以想到和二分图最大点权独立集有关。其次,如果相邻的位置放的蛋相同的话需要付出一定的花费这个又和hdu 3657:Game有些许相似之处。所以我们这么构图:
按照黑白染色把矩阵中的位置分为A,B两个集合,每个集合中的位置拆为k,k'两个点。对于A集合,从源点向k连边,容量为它放金蛋是的收获,从k再向k'连边容量设为inf,从k'连到汇点,容量为其放银弹时的收益。这样设的目的就是为了使得割必须在源点到k或者k'到汇点上。
对于B集合,我们做一些改变,把从源点到k的容量设为放银弹的收益,k'到汇点的容量设为其放金蛋的收益,k和k'之间容量为inf的边不变。至于为什么看下一步。
然后再接下来一步加边就是关键了!我们知道如果A集合中的金蛋和B集合中的金蛋相邻的话会付出一定的话费,为了表示设A集合中的某位置a是金蛋,我们会连接源点->a。为了表示B集合中位置b是金蛋的,我们会连接b'->汇点。好的,在这里我们假设b位置和a相邻。为了表示a位置和b位置同时放金蛋需要G的花费,我们连接a->b'并把其容量设为G,对于银弹我们做同样的处理。至于为什么,好好把hdu3567刷掉就全明白了
接下来,用所有的map1[i][j]+map2[i][j]-最小割得到的就是答案。
程序:
#include"stdio.h"
#include"string.h"
#define M 100005
#define inf 999999999
int min(int a,int b)
{
return a<b?a:b;
}
struct st
{
int u,v,w,next;
}edge[M];
int head[M],work[M],q[M],dis[M],t;
void init()
{
t=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
edge[t].u=u;
edge[t].v=v;
edge[t].w=w;
edge[t].next=head[u];
head[u]=t++;
edge[t].u=v;
edge[t].v=u;
edge[t].w=0;
edge[t].next=head[v];
head[v]=t++;
}
int bfs(int S,int T)
{
int rear=0;
memset(dis,-1,sizeof(dis));
q[rear++]=S;
dis[S]=0;
for(int i=0;i<rear;i++)
{
for(int j=head[q[i]];j!=-1;j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].w&&dis[v]==-1)
{
dis[v]=dis[q[i]]+1;
q[rear++]=v;
if(v==T)
return 1;
}
}
}
return 0;
}
int dfs(int cur,int a,int T)
{
if(cur==T)
return a;
for(int &i=work[cur];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(edge[i].w&&dis[v]==dis[cur]+1)
{
int tt=dfs(v,min(a,edge[i].w),T);
if(tt)
{
edge[i].w-=tt;
edge[i^1].w+=tt;
return tt;
}
}
}
return 0;
}
int Dinic(int S,int T)
{
int ans=0;
while(bfs(S,T))
{
memcpy(work,head,sizeof(head));
while(int tt=dfs(S,inf,T))
ans+=tt;
}
return ans;
}
int main()
{
int n,m,G,S,i,j;
int T,g[66][66],s[66][66];
scanf("%d",&T);
int kk=1;
while(T--)
{
scanf("%d%d%d%d",&n,&m,&G,&S);
int sum=0;
for(i=0;i<n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&g[i][j]);
sum+=g[i][j];
}
}
for(i=0;i<n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&s[i][j]);
sum+=s[i][j];
}
}
init();
for(i=0;i<n;i++)
{
for(j=1;j<=m;j++)
{
if((i+j)&1)
{
add(0,i*m+j,g[i][j]);
add(i*m+j,i*m+j+m*n,inf);
add(i*m+j+m*n,m*n*2+1,s[i][j]);
if(i+1<n)
add(i*m+j,(i+1)*m+j,G);
if(j+1<=m)
add(i*m+j,i*m+j+1,G);
if(i-1>=0)
add(i*m+j,(i-1)*m+j,G);
if(j-1>=1)
add(i*m+j,i*m+j-1,G);
}
else
{
add(0,i*m+j+m*n,s[i][j]);
add(i*m+j+m*n,i*m+j,inf);
add(i*m+j,m*n*2+1,g[i][j]);
if(i+1<n)
add(i*m+j+m*n,(i+1)*m+j+m*n,S);
if(j+1<=m)
add(i*m+j+m*n,i*m+j+1+m*n,S);
if(i-1>=0)
add(i*m+j+m*n,(i-1)*m+j+m*n,S);
if(j-1>=1)
add(i*m+j+m*n,i*m+j-1+m*n,S);
}
}
}
int ans=Dinic(0,m*n*2+1);
printf("Case %d: %d\n",kk++,sum-ans);
}
}