向前走莫回头❤

【校内互侧】日程表(最小割)

日程表
【题目描述】
热情的选手 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);
	 }
}


posted @ 2016-08-19 08:11  lris0-0  阅读(65)  评论(0编辑  收藏  举报
过去的终会化为美满的财富~o( =∩ω∩= )m