FZU 2199 Patchmania I

92 篇文章 0 订阅
86 篇文章 0 订阅

原题中文题面说的很清楚,这里就不转述了
1.选一个萝卜i做起点,求最小距离Ai和最小距离方案数Bi。
2.求覆盖所有萝卜,以洞为终点的路径条数Ci。
答案方案数就是 ∑ i = 1 n B i ∗ C i ∗ [ A i = = m i n A ] \sum_{i=1}^nB_i*C_i*[Ai == minA] i=1nBiCi[Ai==minA]
注意 A i , B i A_i,B_i Ai,Bi在统计路径时路径不能跨过#和O
另外没有萝卜的情况不能输出-1.
然后独立插头的讨论需要格外注意。
AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#define LL long long
#define maxn 10
#define inf 0x3f3f3f3f
#define mod 1000000007
using namespace std;

int n,m,mask[maxn],dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}},tx,ty;
int dis[maxn][maxn],wds[maxn][maxn];
char mp[maxn][maxn];
map<LL,pair<LL,LL> >st[2];
LL encode(int pos)
{	LL ret=0;
	static int id[maxn]={},cnt;
	memset(id,-1,sizeof id),cnt=id[2]=2,id[1]=1,id[0]=0;
	for(int i=pos;i>=0;i--) ret=ret<<4|(id[mask[i]]==-1?id[mask[i]]=++cnt:id[mask[i]]);
	if(pos == m-1) ret<<=4;
	return ret;}
void decode(LL ret){ for(int i=0;i<=m;i++) mask[i]=ret&15,ret>>=4; }
void check2(pair<LL,LL>&a,pair<LL,LL>b)
{	if(a.first > b.first) a=b;
	else if(a.first==b.first) a.second=(a.second+b.second)%mod;}
void check(int now,LL key,pair<LL,LL>b)
{	if(!b.second) return;
	if(st[now].count(key)) check2(st[now][key],b);
	else st[now][key]=b;}
inline bool ck(int x,int y){ return mp[x][y]=='#'||mp[x][y]=='O'; }
void dp(int i,int j,int now)
{
	for(map<LL,pair<LL,LL> >::iterator it=st[now^1].begin();it!=st[now^1].end();it++)
	{	decode((*it).first);pair<LL,LL>val=(*it).second;
		if(mp[i][j] == '.' || mp[i][j] =='X' )
		{
			check(now,encode(j==m?m-1:m),val);
			continue;
		}
		int L=mask[j-1],U=mask[j];
		if(!L && !U)
		{	
			if(ck(i,j+1) && ck(i+1,j)) 
				mask[j-1] = mask[j] = 9 , check(now,encode(j==m?m-1:m),val);
			if(dis[i][j] < inf && mp[i][j]=='#')
			{
				if(ck(i,j+1)) mask[j]=1,mask[j-1]=0,check(now,encode(j==m?m-1:m),make_pair(dis[i][j],1ll*val.second*wds[i][j]%mod));
				if(ck(i+1,j)) mask[j]=0,mask[j-1]=1,check(now,encode(j==m?m-1:m),make_pair(dis[i][j],1ll*val.second*wds[i][j]%mod));
			}
			if(mp[i][j] == 'O')
			{
				if(ck(i,j+1)) mask[j]=2,mask[j-1]=0,check(now,encode(j==m?m-1:m),val);
				if(ck(i+1,j)) mask[j]=0,mask[j-1]=2,check(now,encode(j==m?m-1:m),val);
			}
		}
		else if(L && U)
		{
			if(L == U || (L<=2 && U<=2 && (i!=tx||j!=ty)))  continue;
			int tmp = min(L,U) , res = max(L,U);
			for(int k=0;k<=m;k++) 
				if(mask[k] == res)
					mask[k] = tmp;
			mask[j-1] = mask[j] = 0;
			check(now,encode(j==m?m-1:m),val);
		}
		else
		{
			mask[j] = U+L , mask[j-1] = 0;
			if(ck(i,j+1)) check(now,encode(j==m?m-1:m),val);
			mask[j-1] = U+L , mask[j] = 0;
			if(ck(i+1,j)) check(now,encode(j==m?m-1:m),val);
			
			int tmp = U+L;
			if(tmp == 1 && mp[i][j] == 'O') mask[j-1]=mask[j]=0,check(now,encode(j==m?m-1:m),val);
			if(tmp == 2 && (dis[i][j] < inf && mp[i][j]=='#')) 
				mask[j-1]=mask[j]=0,check(now,encode(j==m?m-1:m),make_pair(dis[i][j],1ll*val.second*wds[i][j]%mod));
			if(tmp > 2)
			{
				if(mp[i][j] == 'O') 
				{
					for(int k=0;k<=n;k++)
						if(mask[k] == tmp)
							mask[k] = 2;
					mask[j-1] = mask[j] = 0;
					check(now,encode(j==m?m-1:m),val);
				}
				if(dis[i][j] < inf && mp[i][j] == '#')
				{
					decode((*it).first);
					for(int k=0;k<=n;k++)
						if(mask[k] == tmp)
							mask[k] = 1;
					mask[j-1] = mask[j] = 0;
					check(now,encode(j==m?m-1:m),make_pair(dis[i][j],1ll*val.second*wds[i][j]%mod));
				}
			}
		}
	}
}

int main()
{
	int T;scanf("%d",&T);
	for(int cas=1;cas<=T;cas++)
	{
		scanf("%d%d",&n,&m);
		memset(mp,0,sizeof mp);
		for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
			
		queue<pair<int,int> >q;
		memset(dis,0x3f,sizeof dis);
		int cnt=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				if(mp[i][j] == 'X') q.push(make_pair(i,j)),dis[i][j]=0;
				if(mp[i][j] == '#' || mp[i][j] == 'O') 
					cnt++,tx=i,ty=j;
			}
		
		queue<pair<int,int> >q2;
		for(int x,y;!q.empty();)
		{
			x=q.front().first,y=q.front().second,q.pop();
			q2.push(make_pair(x,y));
			if(mp[x][y] == '#' || mp[x][y] == 'O')
			{
				continue;
			}
			for(int i=0;i<4;i++)
			{
				int u = dir[i][0] + x,  v = dir[i][1] + y;
				if(u > 0 && u <= n && v > 0 && v <= m && dis[u][v] > dis[x][y] + 1)
				{
					dis[u][v] = dis[x][y] + 1;
					q.push(make_pair(u,v));
				}
			}
		}
		
		memset(wds,0,sizeof wds);
		wds[q2.front().first][q2.front().second] = 1;
		q2.pop();
		for(int x,y;!q2.empty();)
		{
			x=q2.front().first,y=q2.front().second,q2.pop();
			for(int i=0;i<4;i++)
			{
				int u = dir[i][0] + x,  v = dir[i][1] + y;
				if(u > 0 && u <= n && v > 0 && v <= m && dis[u][v] == dis[x][y] - 1 && (mp[u][v]=='.' || mp[u][v]=='X'))
				{
					wds[x][y] = (wds[x][y] + wds[u][v]) % mod;
				}
			}
		}
		
		int now = 1;
		st[0].clear(),st[0][0]=make_pair(inf,1);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				st[now].clear();
				dp(i,j,now);
				now^=1;
			}
			
		if(cnt == 1)  printf("Case #%d: %d %d\n",cas,dis[tx][ty],wds[tx][ty]);
		else if(st[now^1].count(0)) printf("Case #%d: %I64d %I64d\n",cas,st[now^1][0].first+cnt-1,((st[now^1][0].second%mod)+mod)%mod);
		else printf("Case #%d: -1\n",cas);
	}
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值