日程表(最小割)

日程表
【题目描述】
热情的选手 Sphinny 正在看新一年的日程表,并发现已经安排了很多编程竞赛。她将这一年的每一天都用以下三种方式之一在日程表上打标记。
1.   白色:这一天她将不参加竞赛。或许这一天没有预定的竞赛,或许
这一天有更重要的事情要做(生活中肯定还有其他美好的事情) 。
2.   蓝色:这一天她将参加一场竞赛。
3.   问号:这一天有预定好的竞赛,但她还没有决定好是否参加。
为了简化问题,我们假设没有资格的概念:你不必参加一场比赛去取得另一场比赛的参赛资格。
Sphinny 生活的世界与我们的世界有所不同,那个世界里一年有 n 个月每个月恰有 m 天。看她美丽的日程表。Sphinny 认为对于每一天,有四天与它相邻(可能有的不存在):同一个月的前一天,同一个月的后一天,前一个月的同一天,后一个月的同一天。Sphinny 想最大化所有竞赛的喜悦值之和。一场竞赛的喜悦值的计算方式是:
1.   初始为 4。
2.   每有与那一天相邻的一天要参赛,喜悦值减 1。 (你可以认为
Sphinny 喜欢竞赛,但连续参赛让她感觉很累。并且出于审美的原因,在相邻两个月的同一天参赛也不是很好。 )
现在,Sphinny 想计划这一年,并决定把每一个问号标记都改为白色标记或蓝色标记。她的目标很简单,就是最大化总喜悦值。
【输入格式】
第一行一个整数 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


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 1000003
#define M 103
#define inf 1000000000
using namespace std;
int n,m,tot;
int map[M][M],vis[M][M],cnt;
int point[N],next[N],v[N],remain[N],last[N],cur[N],num[N];
int deep[N],belong[M][M];
int xx[10]={0,1,0,-1},yy[10]={1,0,-1,0};
void add(int x,int y,int z)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
	//cout<<x<<" "<<y<<" "<<z<<endl;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; 
}
int addflow(int s,int t)
{
	int now=t; 
	int ans=inf;
	while(now!=s)
	{
		ans=min(ans,remain[last[now]]);
		now=v[last[now]^1];
	}
	now=t;
	while(now!=s)
	{
		remain[last[now]]-=ans;
		remain[last[now]^1]+=ans;
		now=v[last[now]^1];
	}
	return ans;
}
void dfs(int s,int t)
{
	for (int i=s;i<=t;i++) deep[i]=t;
	deep[t]=0; 
	queue<int> p; p.push(t);
	while(!p.empty())
	{
		int now=p.front(); p.pop();
		for (int i=point[now];i!=-1;i=next[i])
		 if (remain[i^1]&&deep[v[i]]==t)
		  deep[v[i]]=deep[now]+1,p.push(v[i]);
	}
}
int isap(int s,int t)
{
	dfs(s,t);
	for (int i=s;i<=t;i++) cur[i]=point[i];
	for (int i=s;i<=t;i++) num[deep[i]]++;
	int now=s; int ans=0;
	while(deep[s]<t)
	{
		if (now==t)
		{
			ans+=addflow(s,t);
			now=s;
		}
		bool f=false;
		for (int i=cur[now];i!=-1;i=next[i])
		 if (deep[v[i]]+1==deep[now]&&remain[i])
		 {
		 	f=true;
		 	last[v[i]]=i;
		 	cur[now]=i;
		 	now=v[i];
		 	break;
		 }
		if (!f)
		{
			int minn=t;
			for (int i=point[now];i!=-1;i=next[i])
			 if (remain[i])  minn=min(minn,deep[v[i]]);
			if (!--num[deep[now]]) break;
			deep[now]=minn+1;
			num[deep[now]]++;
			cur[now]=point[now];
			if (now!=s)
			 now=v[last[now]^1];
		}
	}
	return ans;
}
int calc(int x,int y)
{
	int sum=0;
	for (int i=0;i<4;i++)
	 {
	 	int nowx=x+xx[i];
	 	int nowy=y+yy[i];
	 	if (nowx>0&&nowy>0&&nowx<=n&&nowy<=m&&map[nowx][nowy]!=1)
	 	 sum++;
	 }
	return sum;
}
int calc2(int x,int y)
{
	int sum=0;
	for (int i=0;i<4;i++)
	 {
	 	int nowx=x+xx[i];
	 	int nowy=y+yy[i];
	 	if (nowx>0&&nowy>0&&nowx<=n&&nowy<=m&&map[nowx][nowy]==1)
	 	 sum++;
	 }
	return sum;
}
int main()
{
	freopen("cal.in","r",stdin);
	freopen("cal.out","w",stdout);
	int t; scanf("%d",&t);
	for (int T=1;T<=t;T++)
	{
		scanf("%d%d",&n,&m);
		memset(point,-1,sizeof(point));
		memset(next,-1,sizeof(next));
		memset(num,0,sizeof(num));
		tot=-1;
		cnt=1;
		for (int i=1;i<=n;i++)
		{
			char s[M]; scanf("%s",s+1);
			for (int j=1;j<=m;j++)
			{
				if (s[j]=='#') map[i][j]=1;
				if (s[j]=='.') map[i][j]=-1;
				if (s[j]=='?') map[i][j]=0,belong[i][j]=++cnt;
			}
		}
		for (int i=1;i<=n;i++)
		 for (int j=1;j<=m;j++)
		  if (i%2)  {   if (j%2)  vis[i][j]=1;   }
		  else if (!(j%2)) vis[i][j]=1;
		int ans=0;
		for (int i=1;i<=n;i++)
		 for (int j=1;j<=m;j++)
		  if (map[i][j]==1)  ans+=(4-calc2(i,j));
		int s=1; int t=n*m+2;
		for (int i=1;i<=n;i++)
		 for (int j=1;j<=m;j++)
		  if (!map[i][j])
		   {
		   	ans+=4;
		   	int t1=4;
		   	int t2=2*calc2(i,j);
		   	//cout<<i<<" "<<j<<" "<<t2<<endl;
		   	if (vis[i][j])  add(s,belong[i][j],t1),add(belong[i][j],t,t2);
		   	else add(s,belong[i][j],t2),add(belong[i][j],t,t1);
		   	if (vis[i][j])
		   	{
			    for (int k=0;k<4;k++)
			    {
			    	int nowx=i+xx[k];
				 	int nowy=j+yy[k];
				 	if (nowx>0&&nowy>0&&nowx<=n&&nowy<=m&&!map[nowx][nowy])
				 	 add(belong[i][j],belong[nowx][nowy],2);//cout<<i<<" "<<j<<" "<<nowx<<" "<<nowy<<endl;
			    }
		    }
		   }
		//cout<<ans<<endl;
		printf("Case #%d: %d\n",T,ans-isap(s,t));
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值