【校内互侧】日程表(最小割)
热情的选手 Sphinny 正在看新一年的日程表,并发现已经安排了很多编程竞赛。她将这一年的每一天都用以下三种方式之一在日程表上打标记。
1.白色:这一天她将不参加竞赛。或许这一天没有预定的竞赛,或许这一天有更重要的事情要做(生活中肯定还有其他美好的事情) 。
2.蓝色:这一天她将参加一场竞赛。
3.问号:这一天有预定好的竞赛,但她还没有决定好是否参加。
为了简化问题,我们假设没有资格的概念:你不必参加一场比赛去取得另一场比赛的参赛资格。Sphinny 生活的世界与我们的世界有所不同,那个世界里一年有 n 个月,每个月恰有 m 天。
下面的图片是一张 5 个月,每个月有 8 天的日程表。上面有 15 天标记为蓝色,5 天标记了问号。
看她美丽的日程表。Sphinny 认为对于每一天,有四天与它相邻(可能有的不存在):同一个月的前一天,同一个月的后一天,前一个月的同一天,后一个月的同一天。
Sphinny 想最大化所有竞赛的喜悦值之和。一场竞赛的喜悦值的计算方式是:
1. 初始为 4。
2. 每有与那一天相邻的一天要参赛,喜悦值减 1。 (你可以认为Sphinny 喜欢竞赛,但连续参赛让她感觉很累。并且出于审美的原因,在相邻两个月的同一天参赛也不是很好。 )
现在,Sphinny 想计划这一年,并决定把每一个问号标记都改为白色标记或蓝色标记。她的目标很简单,就是最大化总喜悦值。
下图是上面例子的一种解决方案。通过把 2 个问号标记变为蓝色标记,剩下的 3 个问号标记变为白色标记,她可以得到喜悦值 42。
【输入格式】
第一行一个整数 T 表示测试数据组数。
对于每一组数据:第一行两个整数 n,m 表示有 n 个月,每个月有 m 天。
之后 n 行每行一个长度为 m 的字符串,第 i 行第 j 个字符表示第 i 个月的第 j天的状态, “#”表示这一天被标记为蓝色, “.”表示这一天被标记为白色,“?”表示这一天被标记为问号。
【输出格式】
对于每一组数据,输出:
Case #X: Y X 是第几组测试数据,Y 是最大总喜悦值。
【输入样例】
2
3 3
.?.
.?.
.#.
5 8
.#...##.
.##..?..
.###.#.#
??#..?..
###?#...
【输出样例】
Case #1: 8
Case #2: 42
【数据范围】
对于 30%的数据,1≤n,m≤5
对于 60%的数据,1≤n,m≤15
对于 100%的数据,1≤n,m≤50,1≤T≤100
_________________________________________________________________
【题解】【网络流最小割】
【初始化最大总喜悦值ans为所有蓝点的喜悦值和所有‘?’均为蓝点的喜悦值之和】
【建图,将原网格黑白染色,用所有的‘?’点建图,若是白点,就从源点连一条边权为4的边(表示当前点为蓝点的喜悦值),从当前点向汇点连一条边权为2*tot(tot是当前点四周有几个蓝点,需预处理)的边,再从当前点向周围的‘?’点连一条边权为2的边(表示当这两个相邻的‘?’点都为蓝点的损耗);若是黑点,就从源点向当前点连一条边权为2*tot的边,从当前点向汇点连一条边权为4的点】
【然后跑最大流,最后用ans-求出的最大流】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char ch[60][60];
int d1[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int a[1000010],nxt[1000010],p[1000010],remain[1000010],tot;
int dis[3000],cur[3000],d[1000010],h,t;
int num[60][60],vis[60][60],n,m,time,ans,cnt;
inline void clear()
{
memset(p,-1,sizeof(p));
memset(d,0,sizeof(d));
memset(nxt,-1,sizeof(nxt));
memset(cur,-1,sizeof(cur));
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
ans=cnt=0; tot=-1;
}
inline void add(int x,int y,int v)
{
tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot; remain[tot]=v;
tot++; a[tot]=x; nxt[tot]=p[y]; p[y]=tot; remain[tot]=0;
}
inline int calc(int i,int j)
{
int ss=0;
for(int l=0;l<4;++l)
{
int xx=i+d1[l][0],yy=j+d1[l][1];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&ch[xx][yy]=='#')
ss++;
}
return ss;
}
inline bool bfs()
{
memset(dis,-1,sizeof(dis));
for(int i=0;i<=cnt;++i) cur[i]=p[i];
h=0; t=1; d[1]=0; dis[0]=0;
while(h!=t)
{
h=h%1000010+1;
int u=d[h]; int v=p[u];
while(v!=-1)
{
if(remain[v]&&dis[a[v]]<0)
{
dis[a[v]]=dis[u]+1;
t=t%1000010+1;
d[t]=a[v];
}
v=nxt[v];
}
}
if(dis[cnt]<0) return 0;
else return 1;
}
int dfs(int now,int end,int flow)
{
if(!flow||now==end) return flow;
int u=cur[now],s;
while(u!=-1)
{
cur[now]=u;
if(remain[u]>0&&dis[a[u]]==dis[now]+1&&(s=dfs(a[u],end,min(flow,remain[u]))))
{remain[u]-=s,remain[u^1]+=s;return s;}
u=nxt[u];
}
return 0;
}
int main()
{
freopen("cal.in","r",stdin);
freopen("cal.out","w",stdout);
int i,j,k;
scanf("%d",&time);
for(k=1;k<=time;++k)
{
clear();
scanf("%d%d\n",&n,&m);
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j) scanf("%c",&ch[i][j]);
getchar();
}
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
{
if(ch[i][j]=='#')
ans+=(4-calc(i,j));
if(ch[i][j]=='?')
ans+=4,num[i][j]=calc(i,j),vis[i][j]=++cnt;
}
cnt++;
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
if(vis[i][j])
if((i+j)&1)
{
add(0,vis[i][j],4); add(vis[i][j],cnt,2*num[i][j]);
for(int h=0;h<4;++h)
{
int xx=i+d1[h][0],yy=j+d1[h][1];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&vis[xx][yy]) add(vis[i][j],vis[xx][yy],2);
}
}
else add(0,vis[i][j],2*num[i][j]),add(vis[i][j],cnt,4);
int sum;
while(bfs())
while(sum=dfs(0,cnt,0x7fffffff))
ans-=sum;
printf("Case #%d: %d\n",k,ans);
}
}