JZOJ4020. 【雅礼联考DAY02】Revolution

题意

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

数据

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

Analysis

神奇的网络流二元关系建图(好像是裸题,然而我太弱了,并不会)。考虑一个格子有两个限制,但需要另外的点来限制当前点,那么需要黑白点染色。将一个点拆成 x,y x , y .
x>y x − > y 连一条流量为 vali,j v a l i , j 的边
若为白点, S>x S − > x 连一条流量为 costi,j c o s t i , j 的边
若为黑点, x>T x − > T 如上。
然后白向黑如下连边 x>x1 x − > x 1 连一条 inf i n f y>y1 y − > y 1 连一条 inf i n f
这样图就建好了,用总收益减最小割得到答案。证明一下正确性,要保留 cost c o s t 的边,只有割掉 val v a l 或者将相连黑点的 cost c o s t 割掉,满足上述限制。

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define id(i,j) ((i - 1) * m + j)
const int N = 20 + 5;
const int M = 1e4 + 5;
const int inf = 0x3f3f3f3f;
int c[N][N],val[N][N],q[M],dis[M],dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int to[M << 1],nx[M << 1],flow[M << 1],st[M];
int n,m,ret,ans,S,T,tot = 1;
void add(int u,int v,int w)
{
    to[++tot] = v,nx[tot] = st[u],flow[tot] = w,st[u] = tot;
    to[++tot] = u,nx[tot] = st[v],flow[tot] = 0,st[v] = tot;
}
inline bool bfs()
{
    memset(dis,0xff,sizeof(dis));
    int h = 1,t = 0; q[++t] = S,dis[S] = 0;
    while (h <= t)
    {
        int x = q[h++];
        for (int i = st[x] ; i ; i = nx[i])
        {
            if (~dis[to[i]] || !flow[i]) continue;
            dis[to[i]] = dis[x] + 1,q[++t] = to[i];
        }
    }
    return dis[T] != -1;
}
inline int dfs(int x,int mx)
{
    if (x == T) return mx;
    int ret = 0,now;
    for (int i = st[x] ; i ; i = nx[i])
    if (dis[to[i]] == dis[x] + 1 && flow[i] && (now = dfs(to[i],min(mx,flow[i]))))
    {
        flow[i] -= now,mx -= now;
        flow[i ^ 1] += now,ret += now;
        if (!mx) break;
    }
    if (!ret) dis[x] = -1;
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1 ; i <= n ; ++i)
        for (int j = 1 ; j <= m ; ++j)
        {
            char ch; scanf(" %c",&ch);
            if (ch >= '0' && ch <= '9') c[i][j] = ch - '0';
            if (ch >= 'a' && ch <= 'z') c[i][j] = ch - 'a' + 10;
            if (ch >= 'A' && ch <= 'Z') c[i][j] = ch - 'A' + 36;
        }
    for (int i = 1 ; i <= n ; ++i)
        for (int j = 1 ; j <= m ; ++j)
        {
            char ch; scanf(" %c",&ch);
            if (ch >= '0' && ch <= '9') val[i][j] = ch - '0';
            if (ch >= 'a' && ch <= 'z') val[i][j] = ch - 'a' + 10;
            if (ch >= 'A' && ch <= 'Z') val[i][j] = ch - 'A' + 36;
            ret += val[i][j];
        }
    S = 0,T = n * m * 2 + 1;
    for (int i = 1 ; i <= n ; ++i)
        for (int j = 1 ; j <= m ; ++j)
        {
            if ((i + j) & 1)
            {
                add(id(i,j) + n * m,T,c[i][j]),add(id(i,j),id(i,j) + n * m,val[i][j]);
                continue;
            }
            add(S,id(i,j),c[i][j]),add(id(i,j),id(i,j) + n * m,val[i][j]);
            for (int k = 0 ; k < 4 ; ++k)
            {
                int x = i + dir[k][0],y = j + dir[k][1];
                if (x < 1 || x > n || y < 1 || y > m) continue;
                add(id(i,j),id(x,y),inf),add(id(i,j) + n * m,id(x,y) + n * m,inf);
            }
        }
    while (bfs()) ans += dfs(S,inf);
    printf("%d\n",ret - ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值