HDU-3820-Golden Eggs(最小割 好题)

题目
问题描述
有一个有N行M列的网格。在每个细胞里,你可以选择放入一个金蛋或银蛋,或者让它空着。如果你把一个鸡蛋放进细胞,你会得到一些点这取决于鸡蛋的颜色。但是对于每一对相邻的颜色相同的鸡蛋,如果是金色的就会失去G点,否则就会失去S点。当且仅当两个鸡蛋共享一条边时,两个鸡蛋是相邻的。试着把你的分数写得越高越好。

输入

第一行包含一个整数T,表示测试用例的数量。
在每个测试用例的第一行中有四个整数N, M, G和S。然后是2*N行,每一行包含M个整数。第i行Aij的第j个整数表示如果单元格(i,j)中有一个金蛋,您将得到的点。第j个整数(i+N)-第j行表示如果单元格(i,j)中有一个银蛋,你将得到的点。

Technical Specification

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

输出

对于每个测试用例,首先输出用例号,然后输出一行中的最高点。
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

在这里插入图片描述

#include <queue>
#include <cstring>
#include <cstdio>
#include <iostream>
#define  m(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int N=5000+5;
const int INF=0x3f3f3f3f;
struct Edge{int to,len,nex;}edge[N*7];
int head[N],d[N];
int tot,s,t;
void add(int from,int to,int len)
{
    edge[++tot]=(Edge){to,len,head[from]};head[from]=tot;
    edge[++tot]=(Edge){from,0,head[to]};head[to]=tot;
}
queue<int>q;
bool bfs()
{
    while(!q.empty())
        q.pop();
    m(d,0);
    q.push(s);
    d[s]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=edge[i].nex)
        {
            int y=edge[i].to,l=edge[i].len;
            if(!d[y]&&l)
            {
                d[y]=d[x]+1;
                q.push(y);
                if(y==t)
                    return 1;
            }
        }
    }
    return 0;
}
int dinic(int x,int flow)
{
    if(x==t)
        return flow;
    int res=flow,k;
    for(int i=head[x];i&&res;i=edge[i].nex)
    {
        int y=edge[i].to,l=edge[i].len;
        if(l&&d[y]==d[x]+1)
        {
            k=dinic(y,min(res,l));
            if(!k)
            {
                d[y]=0;
                continue;
            }
            edge[i].len-=k;
            edge[i^1].len+=k;
            res-=k;
        }
    }
    return flow-res;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++)
    {
        m(head,0);
        tot=1;
        int n,m,v1,v2,x;
        ll sum=0;
        scanf("%d%d%d%d",&n,&m,&v1,&v2);
        s=0,t=n*m*2+1;
        int k=n*m;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                int pos=(i-1)*m+j;
                add(pos,pos+k,INF);
                scanf("%d",&x);
                sum+=x;
                if((i+j)&1)
                {
                    add(pos+k,t,x);
                    if(i>1)
                        add(pos,pos+k-m,v2);
                    if(i<n)
                        add(pos,pos+k+m,v2);
                    if(j>1)
                        add(pos,pos+k-1,v2);
                    if(j<m)
                        add(pos,pos+k+1,v2);
                }
                else
                {
                    add(s,pos,x);
                    if(i>1)
                        add(pos,pos+k-m,v1);
                    if(i<n)
                        add(pos,pos+k+m,v1);
                    if(j>1)
                        add(pos,pos+k-1,v1);
                    if(j<m)
                        add(pos,pos+k+1,v1);
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                int pos=(i-1)*m+j;
                scanf("%d",&x);
                sum+=x;
                if((i+j)&1)
                    add(s,pos,x);
                else
                    add(pos+k,t,x);
            }
        }
        ll maxflow=0;
        while(bfs())
            maxflow+=dinic(s,INF);
        printf("Case %d: %lld\n",cas,sum-maxflow);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值