【GDOI2008】彩球游戏

题目大意

给定 n ∗ m n*m n∗m 的起始状态和目标状态,状态中只含有R,B,G三个字符,给定两种操作,分别为:
1.选定一个 2 ∗ 2 2*2 2∗2 的子矩阵,将矩阵顺时针旋转 90 ° 90° 90° 。
2.选定一个 2 ∗ 2 2*2 2∗2 的子矩阵,将矩阵的R变成B,B变成G,G变成R。
求起始状态最少经过多少次操作可以变为目标状态。

Input & Output

【GDOI2008】彩球游戏

数据范围

2 ≤ n , m ≤ 4 2 \le n,m \le 4 2≤n,m≤4

思路

题目中格子个数最多为 16 16 16 个,而每一个格子可以放下 R , G , B R,G,B R,G,B 共 3 3 3 中颜色,所以状态总数为 3 16 = 43046721 3^{16}=43046721 316=43046721 ,可以用桶来记录状态。
所以我们就有有一个经典思路:由起始点出发 b f s bfs bfs 搜索,转移就为两种操作,看到达目标状态时的最小步数。
但是会 T L E 50 TLE 50 TLE50 。
再来观察一下题目,发现起始状态和目标状态都给定了,所以我们可以考虑双向广搜(DBFS) ,就是从起点和终点同时进行搜索(注意:从终点开始的搜索要逆向操作,即逆时针和颜色反着换),同时记录下每种状态到达的最小步数,如果起点和终点的状态重合,就可以输出答案了,时间复杂度快了一倍以上!
然后 T L E 90 TLE90 TLE90 。
我也很疑惑呢。。。
之后发现,转移时要找到 2 ∗ 2 2*2 2∗2 的矩阵的对应压缩后的数,如果暴力求解时间复杂度是 O ( n ∗ m ) O(n*m) O(n∗m) 的,我们有一种 O ( 1 ) O(1) O(1) 的求法:
假设我们要求的是坐标为 ( x , y ) (x,y) (x,y) 的颜色,原状态为 S S S ,如图:
【GDOI2008】彩球游戏
按照压位的顺序,我们可以求出 ( x , y ) (x,y) (x,y) 所代表的位为 ( x ∗ m + y ) (x*m+y) (x∗m+y) (记为 l l l)。
那么我们将 ( x , y ) (x,y) (x,y) 移到最前面去,即 S = S / 3 l S=S/3^{l} S=S/3l ,最后 S m o d    3 S\mod 3 Smod3 即为所求的颜色啦。
每个格子都求一遍,只需求 4 4 4 遍,大大节约了时间复杂度,然后就可以愉快地AC啦ΦωΦ。

AC代码

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
int n,m,tot,st,en;
int a[10][10],f[1000800],p[19];
int d[10][10];
int num[10][10];
bool bz[43100400];
int to[43100400];
bool pd(int x,int y,int i,int j)
{
	if(i>=x&&j>=y&&i-x<=1&&j-y<=1)
	return 1;
	return 0;
}
int updata(int x,int y)
{
	return d[x][y]*num[x][y]+d[x+1][y]*num[x+1][y]+d[x][y+1]*num[x][y+1]+d[x+1][y+1]*num[x+1][y+1];
}
void find(int T,int x,int y)
{
	if(T==0) d[x][y]=0,d[x][y+1]=0;
	else if(T==1) d[x][y]=1,d[x][y+1]=0;
	else if(T==2) d[x][y]=2,d[x][y+1]=0;
	else if(T==3) d[x][y]=0,d[x][y+1]=1;
	else if(T==4) d[x][y]=1,d[x][y+1]=1;
	else if(T==5) d[x][y]=2,d[x][y+1]=1;
	else if(T==6) d[x][y]=0,d[x][y+1]=2;
	else if(T==7) d[x][y]=1,d[x][y+1]=2;
	else if(T==8) d[x][y]=2,d[x][y+1]=2;
	return;
}
int del(int S,int x,int y)
{
	int l=(x-1)*m+(y-1);
	int T=(S/p[l])%9;
	find(T,x,y);
	l=x*m+(y-1);
	T=(S/p[l])%9;
	find(T,x+1,y);
	return updata(x,y);
}
void chan(int x,int y,int id)
{
	if(d[x][y]==0)
	if(id==1)
	d[x][y]=1;
	else
	d[x][y]=2;
	else if(d[x][y]==1)
	if(id==1) 
	d[x][y]=2;
	else
	d[x][y]=0;
	else if(d[x][y]==2)
	if(id==1) 
	d[x][y]=0;
	else
	d[x][y]=1;
}

int change1(int S,int x,int y,int id)
{
    int de=del(S,x,y);
    int T=S-de;
    if(id==1)
    {
    	int t=d[x][y];    
		d[x][y]=d[x+1][y];
        d[x+1][y]=d[x+1][y+1];
        d[x+1][y+1]=d[x][y+1];
        d[x][y+1]=t;
	}
	else
	{
		int t=d[x][y];
		d[x][y]=d[x][y+1];
		d[x][y+1]=d[x+1][y+1];
		d[x+1][y+1]=d[x+1][y];
		d[x+1][y]=t;
	}
    T+=updata(x,y);
    return T;
}
int change2(int S,int x,int y,int id)
{
	int de=del(S,x,y);
	int T=S-de;
	chan(x,y,id),chan(x+1,y,id),chan(x,y+1,id),chan(x+1,y+1,id);
	T+=updata(x,y);
	return T;
}
int bfs()
{
	int h=0,t=1;
	f[t]=st; 
	t++;
	f[t]=en;
	bz[st]=1;
	bz[en]=0;
	to[st]=0;
	to[en]=0;
	while(h!=t)
	{
		h=(h+1)%1000000;
		int S=f[h];
		for(register int i=1;i<n;i++)
		 for(register int j=1;j<m;j++)
		 {
		 	    int T=S;
	            T=change1(T,i,j,bz[S]);
	            if(to[S]!=-1&&to[T]!=-1&&bz[S]!=bz[T])
	            return to[S]+to[T]+1;
		 	    if(to[T]==-1)
		 	    {
		 	      bz[T]=bz[S];
		 		  t=(t+1)%1000000;
		 		  f[t]=T;
		 		  to[T]=to[S]+1;
			    }		 		
			    T=change2(S,i,j,bz[S]);	 
	            if(to[S]!=-1&&to[T]!=-1&&bz[S]!=bz[T])
	            return to[S]+to[T]+1;	
			    if(to[T]==-1)
			    {
			       bz[T]=bz[S];
				   t=(t+1)%1000000;
				   f[t]=T;
				   to[T]=to[S]+1;
			    }
		 }
	}
	return -1;
}
int main()
{
	p[0]=1;
	for(int i=1;i<=16;i++)
	p[i]=p[i-1]*3;
	while(1)
	{
		scanf("%d",&n);
		if(n==0)
		return 0;
		memset(bz,0,sizeof(bz));
		memset(to,-1,sizeof(to));
		st=0,en=0;
		scanf("%d",&m);
		int lst=0;
		num[1][1]=1,lst=1;
		for(int i=1;i<=n;i++)
		 for(int j=1;j<=m;j++)
		 if(!(i==1&&j==1))
		 num[i][j]=lst*3,lst=num[i][j];
		for(register int i=1;i<=n;i++)
		 for(register int j=1;j<=m;j++)
		 {
		 	char ch=getchar();
		 	while(ch!='R'&&ch!='B'&&ch!='G')
		 	ch=getchar();
		 	if(ch=='R') st=st+num[i][j]*0;
		 	if(ch=='B') st=st+num[i][j]*1;
		 	if(ch=='G') st=st+num[i][j]*2;
		 }
		for(register int i=1;i<=n;i++)
		 for(register int j=1;j<=m;j++)
		 {
		 	char ch=getchar();
		 	while(ch!='R'&&ch!='B'&&ch!='G')
		 	ch=getchar();
		 	if(ch=='R') en=en+num[i][j]*0;
		 	if(ch=='B') en=en+num[i][j]*1;
		 	if(ch=='G') en=en+num[i][j]*2;
		 }
		printf("%d\n",bfs());
	}
}
上一篇:Android/IOS 京东手机销售榜(前4名--品牌--型号--分辨率--操作系统--版本号)


下一篇:【创建Comparator对象规定排序秩序】【Java】【P1093 】