jzoj 4020. 【雅礼联考DAY02】Revolution 最小割

Description

地图是个矩形的网格。
可以花费一定金钱在一些格子投资。
被投资的格子或者四连通的格子都被投资的话,我就可以获得该格子的收益。
利益最大化是作为商人的基本准则,但这是计算机的任务,拜托您了。

Input

第一行两个数 n,m(n,m ≤ 20),表示矩形的长和宽。
接下来 n 行,每行是 m 个字符组成的字符串,描述投资的花费。
接下来 n 行,每行是 m 个字符组成的字符串,表示该格子的收益。
花费和收益按照一种奇葩的方式给出:
字符 数
‘0’ -’ 9’ 0-9
‘a’ -’ z’ 10-35
‘A’ -’ Z’ 36-61

Output

一个数,表示收益的和减去投资的和的最大值。

Sample Input

【样例 1】
2 2
21
12
21
12
【样例 2】
2 2
ZZ
ZZ
11
11
【样例 3】
3 3
XXX
XXX
XXX
aaa
aZa
aaa
【样例 4】
2 4
asam
atik
123A
45BC
【样例 5】
9 8
IIIIIIII
IIWWWWII
IIWIIIII
IIWIIIII
IIWWWWII
IIIIIWII
IIIIIWII
IIWWWWII
IIIIIIII
IIIIIIII
II0000II
II0II0II
II0II0II
II0000II
II0II0II
II0II0II
II0000II
IIIIIIII

Data Constraint

n,m ≤ 20.

分析:
一看到这一题,好像文理分科啊,然后按着这个想法推了一下。
首先一块地可以选和不选,选了要花钱,不选就拿不到这块地的价值。所以我们考虑把所有价值加起来,然后用最小割解决。

建图模型,大概是这样的,没写权值的边为 INF I N F
这里写图片描述

首先一个拆成三个点,然后第一个点向第二个点连一条 costi c o s t i 的边,第二个点向第三个点连一条 valuei v a l u e i 的边,这两条边要断掉一个,即选或不选。如果切掉 costi c o s t i 的边,则说明我们买了该点,要减去买的费用;否则为不买,因为我们一开始 ans a n s 是所有价值的和,所以要减去。

还有一种方法可以得到这个权值,那就是把周围的点全部买下来,周围的点记为 j j ,即把costj全部割掉,也就是上图左边的三条边;如果有一个 j j 割的是valuej的边,就是有一个不买,都要割掉 valuei v a l u e i 的边。肯定不可能 valuej v a l u e j costj c o s t j 同时割掉,这样就血亏了,而且也不可能。

然后我们给这个棋盘黑白染色,黑点在一边,白点在一边,然后跑最大流就可以了。

好像拆成两个点也可以,不过数据范围很小。

一开始想法是把 costi c o s t i 放一边, valuei v a l u e i 放另外一边,怎么想都做不出来= =

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>

const int inf=0x3f3f3f3f;

using namespace std;

int n,m,cnt,s,t,ans;
int cost[25][25],value[25][25];
int dis[2007],ls[2007];

queue <int> q;

struct edge{
    int y,c,op,next;
}g[500007];

int calc(char ch)
{
    if ((ch>='0') && (ch<='9')) return (ch-'0');
    if ((ch>='a') && (ch<='z')) return (ch-'a'+10);
    return (ch-'A'+36);
}

void init()
{
    char s[105],ch;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        scanf("%c",ch);
        for (int j=1;j<=m;j++)
        {
            cost[i][j]=calc(s[j-1]);
        }
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        scanf("%c",ch);
        for (int j=1;j<=m;j++)
        {
            value[i][j]=calc(s[j-1]);
            ans+=value[i][j];
        }
    }
}

void add(int x,int y,int w)
{
    g[++cnt]=(edge){y,w,cnt+1,ls[x]};
    ls[x]=cnt;
    g[++cnt]=(edge){x,0,cnt-1,ls[y]};
    ls[y]=cnt;
}

int op(int x,int y)
{
    return (x-1)*m+y-1;
}

bool bfs()
{
    for (int i=s;i<=t;i++) dis[i]=0;
    dis[s]=1;
    while (!q.empty()) q.pop();
    q.push(s);
    while (!q.empty())
    {
        int u=q.front();
        q.pop();
        for (int i=ls[u];i>0;i=g[i].next)
        {
            int v=g[i].y;
            if ((dis[v]==0) && (g[i].c))
            {
                dis[v]=dis[u]+1;
                if (v==t) return true;
                q.push(v);
            }
        }
    }
    return false;
}

int dfs(int x,int maxf)
{
    if ((x==t) || (maxf==0)) return maxf;
    int ret=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((dis[x]+1==dis[y]) && (g[i].c))
        {
            int f=dfs(y,min(maxf-ret,g[i].c));
            g[i].c-=f;
            g[g[i].op].c+=f;
            ret+=f;
        }
    }
    return ret;
}

int main()
{
    init();
    s=0; t=n*m*3+1; 
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            if ((i+j)%2==0)
            {
                int x=op(i,j);
                add(s,x*3+1,inf);
                add(x*3+1,x*3+2,cost[i][j]);
                add(x*3+2,x*3+3,value[i][j]);
                if (i>1) 
                {
                    int y=op(i-1,j);
                    add(x*3+2,y*3+3,inf);
                    add(x*3+3,y*3+2,inf);
                }
                if (i<n)
                {
                    int y=op(i+1,j);
                    add(x*3+2,y*3+3,inf);
                    add(x*3+3,y*3+2,inf);
                }
                if (j>1)
                {
                    int y=op(i,j-1);
                    add(x*3+2,y*3+3,inf);
                    add(x*3+3,y*3+2,inf);
                }
                if (j<m)
                {
                    int y=op(i,j+1);
                    add(x*3+2,y*3+3,inf);
                    add(x*3+3,y*3+2,inf);
                }

            }
            else
            {
                int x=op(i,j);
                add(x*3+1,t,inf);
                add(x*3+2,x*3+1,cost[i][j]);
                add(x*3+3,x*3+2,value[i][j]);
            }
        }
    }
    while (bfs()) ans-=dfs(s,inf);
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值