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 ,即把全部割掉,也就是上图左边的三条边;如果有一个 j j 割的是的边,就是有一个不买,都要割掉 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);
}