HDU 3820 Golden Eggs (最小割)

Golden Eggs

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


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

 

这道题是HDU 1565,1569,3657的加强版。不过只要做过前面那几道,就会发现其实是“换汤不换药”的题。加强的地方在于,每一个方格的数不是固定的了,它可以是金蛋也可以是银蛋。那么对于任意一点,要放金蛋还是银蛋呢?其实这就是最小割的模型,二者选一个小的,最后结果是总权值-最小割。所以处理方法是拆点,对于每一个位置,拆出一个点,一个表示放的是金蛋,拆出来的表示放银蛋,对于这两个点连边,最后结果不就是求出二者中小的那个吗?所以,把横纵坐标之和为偶数的点表示放了金蛋,与源点连接,权值为放金蛋的值,然后把这些点拆开,拆出来的放银蛋,与汇点连权值为放银蛋的值。最后这些点对应连一条权值为无穷大的边。然后,把横纵坐标之和为奇数的点表示放了银蛋,也与源点连边,权值为放银蛋的值,拆出来的点和汇点连接权值为放金蛋的边,对应的点也连一条无穷大的边。最后把横纵坐标之和为偶数的且放金蛋的点与横纵坐标之和为奇数的放金蛋的相连,权值为G,横纵坐标之和为奇数的且放银蛋的点和横纵坐标之和为偶数的放银蛋的点相连,权值为S。

既(1)横纵坐标之和为偶数的点的集合为d,把他们与源点连金蛋的值,拆点为d',把他们与汇点连银蛋的值,再d-d'无穷;

(2)横纵坐标之和为奇数的点的集合为s,把他们与源点连银蛋的值,拆点为s',把他们与汇点连金蛋的值,再s-s'无穷;

(3)把d-s'连权值为G的边,s-d'连权值为S的边。

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define SIZE 5005
#define inf 0xfffffff

using namespace std;

struct node
{
    int to,val,next;
}edge[SIZE*15];

int N,M,G,S,sc,sk,pt,sum;
int head[SIZE],idx;
int gap[SIZE],dis[SIZE];
int gold[64][64],silver[64][64];

void addnode(int from,int to,int val)
{
    edge[idx].to = to;
    edge[idx].val = val;
    edge[idx].next = head[from];
    head[from] = idx ++;
    edge[idx].to = from;
    edge[idx].val = 0;
    edge[idx].next= head[to];
    head[to] = idx ++;
}

int dfs(int cur,int cval)
{
    if(cur == sk)
        return cval;
    int mindis = pt - 1, tval = cval;
    for(int i=head[cur]; i!=-1; i=edge[i].next)
    {
        int to = edge[i].to;
        if(edge[i].val > 0)
        {
            if(dis[to] + 1 == dis[cur])
            {
                int val = dfs(to,min(edge[i].val,tval));
                tval -= val;
                edge[i].val -= val;
                edge[i^1].val += val;
                if(dis[sc] >= pt)
                    return cval - tval;
                if(!tval)
                    break;
            }
            if(mindis > dis[to])
                mindis = dis[to];
        }
    }
    if(cval == tval)
    {
        --gap[dis[cur]];
        if(!gap[dis[cur]])
            dis[sc] = pt;
        dis[cur] = mindis + 1;
        ++gap[dis[cur]];
    }
    return cval - tval;
}

void sap()
{
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    int ret = 0;
    gap[sc] = pt;
    while(dis[sc] < pt)
        ret += dfs(sc,inf);
    printf("%d\n",sum-ret);
}

void read()
{
    sum = 0;
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=M; j++)
        {
            scanf("%d",&gold[i][j]);
            sum += gold[i][j];
        }
    }
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=M; j++)
        {
            scanf("%d",&silver[i][j]);
            sum += silver[i][j];
        }
    }
    sc = 0, sk = N*M*2+1, pt = sk+1;
    idx = 0;
    memset(head,-1,sizeof(head));
    int pos;
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=M; j++)
        {
            pos = (i-1)*M+j;
            if((i+j)%2)
            {
                addnode(sc,pos,silver[i][j]);
                addnode(pos,pos+N*M,inf);
                addnode(pos+N*M,sk,gold[i][j]);
                if(i - 1 >= 1) addnode(pos,pos+N*M-M,S);
                if(i + 1 <= N) addnode(pos,pos+N*M+M,S);
                if(j - 1 >= 1) addnode(pos,pos+N*M-1,S);
                if(j + 1 <= M) addnode(pos,pos+N*M+1,S);
            }
            else
            {
                addnode(sc,pos,gold[i][j]);
                addnode(pos,pos+N*M,inf);
                addnode(pos+N*M,sk,silver[i][j]);
                if(i - 1 >= 1) addnode(pos,pos+N*M-M,G);
                if(i + 1 <= N) addnode(pos,pos+N*M+M,G);
                if(j - 1 >= 1) addnode(pos,pos+N*M-1,G);
                if(j + 1 <= M) addnode(pos,pos+N*M+1,G);
            }
        }
    }
}

int main()
{
    int Case;
    scanf("%d",&Case);
    for(int ca = 1; ca <= Case; ca++)
    {
        scanf("%d%d%d%d",&N,&M,&G,&S);
        read();
        printf("Case %d: ",ca);
        sap();
    }
    return 0;
}


 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值