最大流最小割经典例题_最大流最小割的综合运用

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

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

}

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

{

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

{

for(j=1;j<=m;j++)

{

scanf("%d",&g[i][j]);

sum+=g[i][j];

}

}

for(i=0;i

{

for(j=1;j<=m;j++)

{

scanf("%d",&s[i][j]);

sum+=s[i][j];

}

}

init();

for(i=0;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

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

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);

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值