6/26~6/28

1.万圣节后的早晨uva1601

1.该题最大的新颖之处就是可以有三个鬼同时存在的情况,即三个鬼可以同时移动。这就带来了可扩展状态的增多,因此选择bfs
2.在处理三鬼移动的状态时,需要三个循环嵌套,要考虑到鬼可以不动!!同时在进行鬼的位置是否在一步之内交换和鬼的位置互相重合的判断中,可能出现b,c鬼并不存在,但二者的初始坐标相等导致判断错误的情况,因此需要对不存在的鬼的初始坐标进行特殊处理
3.关于搜索中的剪枝:在三种鬼的移动中,只要有一个鬼的坐标不符合要求,当前的枚举就是无意义的,可退出,这样就大大降低了搜索树的复杂度。同时注意到,该图中#占据了大多数,可以走的空格是一个连通块,因此也可以将可走的路建图,并在这上面进行搜索。另一种方法是双向bfs,此处不多说

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2 * 1e5;
#define M  1e6
int w,h,n;
struct node{//存坐标 
	int x,y;
	node(int xx=0,int yy=0){
		x = xx;
		y = yy;
	}
};
node st[4],en[4];
char m[100][100]; 
bool vis[50000005];
struct state{//存状态 
	node a,b,c;
	int step;
	state() {}
	state(node aa,node bb,node cc,int ss){
		a = aa;
		b = bb;
		c = cc;
		step = ss;
	}
}sta[50000005];
int dx[5] = {0,0,-1,0,1};
int dy[5] = {0,1,0,-1,0};
int hashghost(int x1,int y1,int x2,int y2,int x3,int y3){//哈希 
	long long sum = 0;
	sum += x1;
	sum *= 17;
	sum += y1;
	sum *= 17;
	sum += x2;
	sum *= 17;
	sum += y2;
	sum *= 17;
	sum += x3;
	sum *= 17;
	sum += y3;
	return sum;
}
bool check(int x,int y){
	return x >= 1 && x <= w && y >= 1 && y <= h && m[x][y] != '#';
}
int main(){
	char a;
	while(cin >> h >> w >> n){
		if(h == 0 )break;
		a = getchar();
		for(int i = 1; i <= w; i++){
			fgets(m[i] + 1,20,stdin);
		}
		for(int i = 1; i <= w; i++){
			for(int j = 1; j <= h; j++){
				if(m[i][j] == 'a'){st[1].x = i;st[1].y = j;}
				else if(m[i][j] == 'b'){st[2].x = i;st[2].y = j;}//找起止点
				else if(m[i][j] == 'c'){st[3].x = i;st[3].y = j;}
				else if(m[i][j] == 'A'){en[1].x = i;en[1].y = j;}
				else if(m[i][j] == 'B'){en[2].x = i;en[2].y = j;}
				else if(m[i][j] == 'C'){en[3].x = i;en[3].y = j;}
			}
		}	
		memset(sta,0,sizeof sta);
		if(n == 1){
			st[3].x = en[3].x = st[2].x = st[2].y = en[2].x = en[2].y = 0;
			en[3].y = st[3].y = 1;
		}
		else if(n == 2){
			en[3].x = st[3].x = 0;
			en[3].y = st[3].y = 1;
		}
		int beg = hashghost(st[1].x,st[1].y,st[2].x,st[2].y,st[3].x,st[3].y);
		sta[beg] = state(st[1],st[2],st[3],0);
		int end = hashghost(en[1].x,en[1].y,en[2].x,en[2].y,en[3].x,en[3].y);
		sta[end] = state(en[1],en[2],en[3],-1);
		queue<int> q;
		q.push(beg);
		memset(vis,0,sizeof vis);
		vis[beg] = 1;
		while(!q.empty()){
		int now = q.front();
		q.pop();
		node a = sta[now].a;
		node b = sta[now].b;
		node c = sta[now].c;
		int s = sta[now].step;
		for(int i = 0; i < 5; i++){
			int ax = a.x + dx[i];
			int ay = a.y + dy[i];
			if(!check(ax,ay))
				continue;
			for(int j = 0; j < 5; j++){
				int bx = 0,by = 0;
				if(b.x != 0 && b.y != 0){
					bx = b.x + dx[j];
					by = b.y + dy[j];
					if(!check(bx,by))
						continue;
				}
				for(int k = 0; k < 5; k++){
					int cx = 0,cy = 1;
					if(c.x != 0 && c.y != 0){
						cx = c.x + dx[k];
						cy = c.y + dy[k];
						if(!check(cx,cy))
							continue;
					}
					if(!((ax == b.x && ay == b.y) && (bx == a.x && by == a.y)) && !((ax == c.x && ay == c.y) && (cx == a.x && cy == a.y))
					&& !((bx == c.x && by == c.y) && (cx == b.x && cy == b.y)) && (ax != bx || ay != by) && (ax != cx || ay != cy) && (bx != cx || by != cy)){//判是否交换 
						bool flag = 1;
						if(flag){//是否合法 
							int u = hashghost(ax,ay,bx,by,cx,cy);
							if(!vis[u]){
								vis[u] = 1;
								q.push(u);
								sta[u] = state(node(ax,ay),node(bx,by),node(cx,cy),s + 1);
							}
						}
					}
				}
			}
		}
	}
	cout << sta[end].step << endl;;
	}
} 

2.天平难题uva1354

(其实这个题感觉上有点像uva839天平的拓展,这个题同样也需要用到那个题二叉树的思想,只是嵌套进了搜索)

  1. 题目中的天平很像二叉树的结构,因此可以考虑用一个二叉树来描述天平
  2. 那么,首要任务就是枚举二叉树,这对于搜索似乎并不好处理。一开始想到用传统的结构体的方式来进行存储,但这种存储的方式在搜索的操作中并不好进行处理。考虑到总结点个数并不大,尽管用数组存储会有冗余的空间,但消耗不大,也便于操作。查阅题解,得到其中一种方法:用一维数组存储每个节点,下标为节点编号,i * 2为左子节点编号,i* 2 + 1为右子节点编号,该元素的值若为a,正数,则表示该节点挂了个重量为a的吊坠,若为0,则什么也没有,若为-1,则表示这里有一根木棍
  3. 采用dfs来枚举树。三个参数,step(当前处理的节点编号),leaf(还有多少个可放置的节点没有被处理),num(还有多少个吊坠没有得到处理)
  4. 容易得出,当leaf和num都变成0时,无法进行操作,此时step-1为最终节点个数
  5. 对于每个节点,可以放置木棍,也可以放置吊坠。如果放置木棍,则leaf会+1,num不变,如果放置一个吊坠,则leaf和num都-1.对于树的根节点,为了枚举出一个树,应当在此处放一个木棍(如果一共只有一个吊坠,那么根节点处放上该吊坠即可,这是特殊情况,要特判)
  6. dfs中的剪枝:可以发现,如果leaf >= num,那么在这个节点上放置一个木棍会增加leaf,并没有什么用。因此当放置木棍时,一定要leaf < num。另外,如果num和leaf中有任意一个为0,但剩下的变量没有归零,这一次的枚举就是失败的,也可以直接return
  7. 当枚举完一棵树后,应借鉴“天平”的思想,用一个update函数统计树中每个节点及其子树的总重量,便于计算最后每条木棍的长度
  8. 最后一步,计算天平的总宽度,关于这里是怎么实现的,暂时没有看懂
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100000;
int tree[MAXN];//建树,存储枚举的二叉树,节点的左子节点为i* 2 ,右子节点为i* 2 + 1
//树中,值为-1表示是木棍防止处,0表示无东西放置,大于0表示放置了一个重量为a的吊坠
int w[MAXN],val[MAXN];
int s;
double ans,r,lleft,rright;
bool vis[MAXN];
int read(){
	int a;
	scanf("%d",&a);
	return a; 
}
void findedge(double mid,int cur){//确定每一条边的长度,cur为当前正处理的子天平的父节点 
	int a = cur * 2;
	int b = cur * 2 + 1; 
	double nl = mid - ((double)val[b] / (val[a] + val[b]));
	double nr = mid + ((double)val[a] / (val[a] + val[b]));//计算左右偏移量,不过为什么要这么算?????????
	lleft = min(lleft,nl);
	rright = max(rright,nr);//?????????????????????????????
	if(tree[cur] < 0){
		findedge(nl,a);
		findedge(nr,b);//若是木棍,递归余下的子节点 
	} 
} 
void update(int num){//统计每个节点的总重量 
	memset(val,0,sizeof val);
	for(int i = num; i ; --i){//从后往前枚举,即从叶节点自下而上枚举 
		if(tree[i] > 0){
			val[i] = w[tree[i]];
		}
	}////////////////////////////////////////////////////////////////////////////////////
	for(int i = num; i ; --i){
		if(tree[i] == -1){
			val[i] = val[2 * i] + val[2 * i + 1];
		}
	}
	lleft = DBL_MAX;//能够表示的最大,最小浮点数 
	rright = DBL_MIN;
	findedge(0,1);//从根节点算起,以他为原点
	double fin = rright - lleft;//总宽度 
	if(fin < r) {
		ans = max(ans,fin);
	}
} 
void dfs(int step,int leaf,int node){//枚举二叉树,step表示当前节点编号,leaf表示剩余能够扩展1节点,node表示能够放置的吊坠的个数
	if(leaf == 0 && node == 0){//无地方放置,且无吊坠放置,自然枚举完毕
		update(step - 1);//step-1为最终放置的节点个数
		return;  
	} 
	if((node &&!leaf) || (!node && leaf))return;//如果可放置节点合吊坠其中一个为0,则无法继续放置 
	if(tree[step / 2] != -1){
		dfs(step + 1,leaf,node);//如果根节点没有放置木棍,则该子节点也不能放置吊坠 
		return;//为什么回溯	
	} 
	if(leaf < node){//放置木棍会增加可放置节点的个数,因此如果可放置吊坠小于可放置节点的个数,该操作无意义 
		tree[step] = -1;//假设该节点放置木棍;
		dfs(step + 1,leaf + 1,node);//放置木棍,可以导致可放置总结点加1。由于总吊坠最多为10, 
		tree[step] = 0;//回溯
	}
	for(int i = 1; i <= s; ++i){
		if(!vis[i]){
			vis[i] = 1;
			tree[step] = i;
			dfs(step + 1,leaf - 1,node - 1);//放置吊坠,会使二者都减一
			vis[i] = 0;
			tree[step] = 0; 
		}
	}
} 
int main(){
	//freopen("B.out","w",stdout);
	int n;
	cin >> n;
	while(n--){
		ans = -1;
		memset(vis,0,sizeof vis);
		scanf("%lf%d",&r,&s);
		for(int i = 1; i <= s; ++i){
			cin >> w[i];
		}
		if(s == 1){//特判,只有一个吊坠 
			puts("0.0000000000000000");//输出并换行
			continue; 
		}
		tree[1] = -1;//初始化,根节点为木棍
		dfs(2,2,s);
		if(ans == -1)printf("-1\n");
		else printf("%.16lf\n",ans); 
	}
	return 0;
} 

3.洛谷p1127词链

题目大意:用给出的单词组成一个字典序最小的,单词之间首尾字母相同的链条,若不行,则输出-1
1.容易看出,该题是一个典型的欧拉路径+搜索的题目,因此可以通过判断该单词组是否存在欧拉路径来判断能否构成词链。(以单词为边,字母为节点)

ps:1. 组成欧拉路径的条件:有向图中任意节点的出、入度相同或只有一个节点的出度比入度大一(起点)且有一个点的入度比出度大一(终点),无向图中,只有两个节点的度为奇数
2. 组成欧拉回路的条件:有向图中任意节点的出入度相等,无向图中任意节点的度为偶数

2.判断完是否存在欧拉路径后,可得出起点和终点,并以此开始搜索(当然也可能没有起点和终点,是个欧拉回路,这一点也应判断)

(码就不上了,似乎还有一点没搞定)

4.宝箱

题目大意:两种宝物,体积、价值分别为s1,v1,s2,v2,有一个箱子,容积为n,问最多能装下价值为多少的宝箱

  1. 容易想到,枚举宝物1的个数,然后尽可能多地拿宝物2,即枚举n/s1次。但当两者相差极为悬殊的时候,这种方法的效率就会很低
  2. 另一种思路,假如宝物1和宝物2的体积相等,则价值分别为s2 * v1和s1 * v2,此时如果前者比较大,那么宝物2最多拿s1-2即可,不然就可以用前者代替,反之亦然,这样,效率得以大大提高
    审题,并找出题目中的隐含条件
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e3; 
int n,s1,v1,s2,v2;
long long ans;
signed main(){
	//freopen("gmon.out","r",stdin);
	//freopen("B.out","w",stdout);
	int t;
	cin >> t;
	int cnt = 1;
	while(t--){
		ans = 0;
		int m;
		cin >> n >> s1 >> v1 >> s2 >> v2;
		int k = n / s2;
		if(k > 6 * 1e4){
			if(v1 * s2 >= v2 * s1){
				for(int i = 0; i < s1; i++){
					int p = i * s2;
					int q = (n - p) / s1;
					m = i * v2 + q * v1;
					ans = max(ans,m);
				}
				
			}
			else{
				for(int i = 0; i < s2; i++){
					int p = i * s1;
					int q = (n - p) / s2;
					m = i * v1 + q * v2;
					ans = max(ans,m);
				}
			}
		}
		else {
			for(int i = 0; i <= k; i++){
				int p = i * s2;
				int q = (n - p) / s1;
				m = i * v2 + q * v1;
				ans = max(ans,m);
			}
		}
		printf("Case #%lld: %lld\n",cnt++,ans);
	}
}

5.旋转游戏

1.正解只能为IDA*?为什么我写的bfs超时那么多?
2.有三个数,因此可以对三个数分别求解,最后在得到最终答案,求解过程中,将当前处理的数设为1,其他数全设为0,这样可以减少很多状态,算是一个剪枝的不错方法了
3.在做本题的过程中,围绕更便捷地存储状态更换了很多方法,大致有两类:(1)用数组存储当前处理的01串, 并用这个01串在所有状态中的字典序排位来表示它,在计算排位中发现了一个有趣的性质在杨辉三角第i行第j列的数即为Cij的值。(2)将01串视为一个二进制数,将它转换为一个十进制数,用这个十进制数来表示它的状态
4.在(2)的思想基础上,我们可以发现,采用位运算可以很便捷地进行移位处理,这也是在做题中的一个发现
5.该题还有一个麻烦的地方就是竖着的两列的编号不连续,这里可以通过预处理来提高效率。用于旋转的几个函数也因此更容易实现
本人代码(1)

点击查看代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int MAXN = 30;
const int M = 1e5;
const int END = 41960;
int r[MAXN];
int a[9] = {0,1,3,7,12,16,21,23};
int b[9] = {0,2,4,9,13,18,22,24};
int c[9] = {0,5,6,7,8,9,10,11};
int d[9] = {0,14,15,16,17,18,19,20};
int dp[MAXN][MAXN]; 
int f[2 * 1e8];

void get(int m[]){
	int sum = 0;
	int now = 1;
	for(int i = 1;i < 25; i++){
		if(m[i])sum += now;
		now *= 2;
	}
}
void heng(int m[],char bb,bool cc,int n[]){//该函数控制横行的旋转问题,b表示转哪一行,c为1时,往左旋转,c为0时,往右旋转 
	int change[MAXN];
	for(int i = 1; i <= 24; i++)n[i] = m[i];
	if(cc){
		if(bb == 'C'){
			for(int i = 1; i < 7; i++){
				change[i] = m[c[i + 1]];
			}
			change[7] = m[c[1]];
		}
		else if(bb == 'D'){
			for(int i = 1; i < 7; i++){
				change[i] = m[d[i + 1]];
			}
			change[7] = m[d[1]];
		}
	}
	else{
		if(bb == 'C'){
			for(int i = 2; i <=7; i++){
				change[i] = m[c[i - 1]];
			}
			change[1] = m[c[7]];
		}
		if(bb == 'D'){
			for(int i = 2; i <=7; i++){
				change[i] = m[d[i - 1]];
			}
			change[1] = m[d[7]];
		}
	}
	for(int i = 1; i <= 7; i++){
		if(bb == 'C')n[c[i]] = change[i];
		else if(bb == 'D')n[d[i]] = change[i];
	}
} 
void shu(int m[],char bb,bool cc,int n[]){//该函数控制竖列的旋转问题,b表示转哪一列,c为1,则向下转,c为0,则向上转 
	int change[MAXN];
	for(int i = 1; i <= 24; i++){
		n[i] = m[i];
	}
	if(cc){
		if(bb == 'A'){
			for(int i = 2; i <= 7; i++){
				change[i] = m[a[i - 1]];
			} 
			change[1] = m[a[7]];
		}
		else if(bb == 'B'){
			for(int i = 2; i <= 7; i++){
				change[i] = m[b[i - 1]];
			} 
			change[1] = m[b[7]];
		}
	}
	else{
		if(bb == 'A'){
			for(int i = 1; i < 7; i++){
				change[i] = m[a[i + 1]];
			} 
			change[7] = m[a[1]];
		}
		else if(bb == 'B'){
			for(int i = 1; i < 7; i++){
				change[i] = m[b[i + 1]];
			} 
			change[7] = m[b[1]];
		}
	}
	for(int i = 1; i <= 7; i++){
		if(bb == 'A')n[a[i]] = change[i];
		else if(bb == 'B')n[b[i]] = change[i];
	}
}
int ing[MAXN],state[9 * M][MAXN];//ing为当前处理的情况 
int change[MAXN];
char l[MAXN]; 
vector<char> step[9 * M];//记录到达每个状态的步骤 
bool vis[8 * M];
void init(){
	memset(change,0,sizeof change); 
	memset(state,0,sizeof state);
	memset(vis,0,sizeof vis);
	for(int i = 1; i < 9 * M; i++)step[i].clear();
}
int chang(int n[]){//计算该排列的位置 ,n为当前的排列 ,fact为阶乘 
	long long cnt = 8,ord = 0;;
	for(int i = 1; i < 25; i++){
		if(n[i] == 1){
			ord += dp[24 - i][cnt];
			cnt--;
		}
	} 
	return ord;
}
int o[MAXN];
void dfs(int cnt,int now){//预处理,枚举排列 
	if(now == 8 || cnt == 25){
		if(now == 8){
			int m = get(o);
			int n = chang(o);
			f[m] = n;
		}
		
		return;
	}
	o[cnt] = 1;
	dfs(cnt+1,now+1);
	o[cnt] = 0;
	dfs(cnt + 1,now);
}
signed main(){
	dp[0][0] = 1;
	for(int i = 1; i <= 24; i++){//跑杨辉三角,计算组合数 
		for(int j = 0; j <= 24;j++){
			if(j == 0)dp[i][j] = 1;
			else dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
		}
	}
	
	//freopen("B.out","w",stdout);
	while(1){
		scanf("%d",&r[1]);
		if(r[1] == 0)break;
		for(int i = 2; i < 25; i++)scanf("%d",&r[i]);
		queue<int> q;
		string s = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
		int ans;
		for(int i = 2; i <= 3; i++){//处理1,2,3每一种情况 
			while(!q.empty())q.pop();
			init();
			string ss;
			for(int j = 1; j <= 24; j++){//将除了当前处理数字以外的数字变为0 
				if(r[j] == i)change[j] = 1;
			}
			int x = chang(change);
			for(int j = 1; j < 25; j++){
				state[x][j] = change[j];
			}
			q.push(x);
			vis[x] = 1;
			while(!q.empty()){//bfs
				int now = q.front();
				q.pop();
				if(now == END){//搜索结束 
					for(int j = 0; j < step[now].size(); j++){
						ss += step[now][j];
					}
					if(ss.size() < s.size()){
						ans = i;
						s = ss;
						
					}
					else if(ss.size() == s.size()){
						if(ss  < s){
							ans = i;
							s = ss;
						}
					}
					//cout << ss << " " << ans << "\n";
					break;
				}
//				for(int j = 0; j < step[now].size(); j++){
//					cout << step[now][j];
//				}
//				cout << "\n";
				int y;
					shu(state[now],'A',0,ing);//纵向向上移动第一列;
					y = chang(ing);
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1;k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);
						step[y].push_back('A');
					}
					
					shu(state[now],'B',0,ing);//纵向向上移动第二列;
					y = chang(ing);
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1;k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);
						step[y].push_back('B');
					}
					
					heng(state[now],'C',0,ing);//横向向右移动第一行,将state【now】进行操作,并将结果转到ing中 
					y = chang(ing);//新得到结果的位置 
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1; k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);//复制步骤 
						step[y].push_back('C');
					}
				
					heng(state[now],'D',0,ing);//横向向右移动第二行 
					y = chang(ing);
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1; k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);//复制步骤 
						step[y].push_back('D');//添加当前进行的步骤 	
					}
					
					
					shu(state[now],'B',1,ing);//纵向向下移动第二列
					y = chang(ing);
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1;k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);
						step[y].push_back('E');
					}
					
					shu(state[now],'A',1,ing);//纵向向下移动第一列;
					y = chang(ing);
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1;k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);
						step[y].push_back('F');
					}
					
					heng(state[now],'D',1,ing);//横向向左移动第二行 
					y = chang(ing);
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1; k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);//复制步骤 
						step[y].push_back('G');//添加当前进行的步骤 	
					}
					
					heng(state[now],'C',1,ing);//横向向左移动第一行,将state【now】进行操作,并将结果转到ing中 
					y = chang(ing);//新得到结果的位置 
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 1; k < 25; k++)state[y][k] = ing[k];
						for(int k = 0; k < step[now].size(); k++)step[y].push_back(step[now][k]);//复制步骤 
						step[y].push_back('H');
					}
			}
		}
		cout << s << "\n";
		cout << ans << "\n";
	}
}

本人代码(2)

点击查看代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int MAXN = 30;
const int M = 1e7;
const int END = 235968;
int r[MAXN];
int a[9] = {0,1,4,64,2048,32768,1048576,4194304};
int b[9] = {0,2,8,256,4096,131072,2097152,8388608};
int c[9] = {0,16,32,64,128,256,512,1024};
int d[9] = {0,8192,16384,32768,65536,131072,262144,524288};
int f[40000000];
int heng(int m,char bb,bool cc){//该函数控制横行的旋转问题,b表示转哪一行,c为1时,往左旋转,c为0时,往右旋转 ,进行位运算操作 
	int n = m;
	if(cc){
		if(bb == 'C'){
			for(int i = 1; i < 7; i++){
				if(!(m & c[i + 1]) && (m & c[i]))n -= c[i];
				if((m & c[i + 1]) && !(m & c[i]))n += c[i];
			}
			if(!(m & c[1]) && (m & c[7]))n -= c[7];
			if((m & c[1]) && !(m & c[7]))n += c[7];
		}
		else if(bb == 'D'){
			for(int i = 1; i < 7; i++){
				if(!(m & d[i + 1]) && (m & d[i]))n -= d[i];
				if((m & d[i + 1]) && !(m & d[i]))n += d[i];
			}
			if(!(m & d[1]) && (m & d[7]))n -= d[7];
			if((m & d[1]) && !(m & d[7]))n += d[7];
		}
	}
	else{
		if(bb == 'C'){
			for(int i = 2; i <=7; i++){
				if(!(m & c[i - 1]) && (m & c[i]))n -= c[i]; 
				if((m & c[i - 1]) && !(m & c[i]))n += c[i];
			}
			if(!(m & c[7]) && (m & c[1]))n -= c[1];
			if((m & c[7]) && !(m & c[1]))n += c[1];
		}
		if(bb == 'D'){
			for(int i = 2; i <=7; i++){
				if(!(m & d[i - 1]) && (m & d[i]))n -= d[i]; 
				if((m & d[i - 1]) && !(m & d[i]))n += d[i];
			}
			if(!(m & d[7]) && (m & d[1]))n -= d[1];
			if((m & d[7]) && !(m & d[1]))n += d[1];
		}
	}
	return n;
} 
int shu(int m,char bb,bool cc){//该函数控制竖列的旋转问题,b表示转哪一列,c为1,则向下转,c为0,则向上转 
	int n = m;
	if(cc){
		if(bb == 'A'){
			for(int i = 2; i <= 7; i++){
				if(!(m & a[i - 1]) && (m & a[i]))n -= a[i];
				if((m & a[i - 1]) && !(m & a[i]))n += a[i];
			} 
			if(!(m & a[7]) && (m & a[1]))n -= a[1];
			if((m & a[7]) && !(m & a[1]))n += a[1];
		}
		else if(bb == 'B'){
			for(int i = 2; i <= 7; i++){
				if(!(m & b[i - 1]) && (m & b[i]))n -= b[i];
				if((m & b[i - 1]) && !(m & b[i]))n += b[i];
			} 
			if(!(m & b[7]) && (m & b[1]))n -= b[1];
			if((m & b[7]) && !(m & b[1]))n += b[1];
		}
	}
	else{
		if(bb == 'A'){
			for(int i = 1; i < 7; i++){
				if(!(m & a[i + 1]) && (m & a[i]))n -= a[i];
				if((m & a[i + 1]) && !(m & a[i]))n += a[i];
			} 
			if(!(m & a[1])&& (m & a[7]))n -= a[7];
			if((m & a[1])&& !(m & a[7]))n += a[7];
		}
		else if(bb == 'B'){
			for(int i = 1; i < 7; i++){
				if(!(m & b[i + 1]) && (m & b[i]))n -= b[i];
				if((m & b[i + 1]) && !(m & b[i]))n += b[i];
			} 
			if(!(m & b[1]) && (m & b[7]))n -= b[7];
			if((m & b[1]) && !(m & b[7]))n += b[7];
		}
	}
	return n;
}
int change[MAXN];
char l[MAXN]; 
string step[4*M];//记录到达每个状态的步骤 
bool vis[4*M];
void init(){
	memset(change,0,sizeof change); 
	memset(vis,0,sizeof vis);
	for(int i = 1; i < 2 * M; i++)step[i].clear();
}
int get(int m[]){//二进制转十进制 
	int sum = 0,now = 1;
	for(int i = 1; i < 25; i++){
		sum += now * m[i];
		now *= 2;
	}
	return sum;
}
signed main(){
	freopen("B.out","w",stdout);
	while(1){
		scanf("%d",&r[1]);
		if(r[1] == 0)break;
		for(int i = 2; i < 25; i++)scanf("%d",&r[i]);
		queue<int> q;
		string s = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
		int ans;
		for(int i = 1; i <= 3; i++){//处理1,2,3每一种情况 
			while(!q.empty())q.pop();
			init();
			string ss;
			for(int j = 1; j <= 24; j++){//将除了当前处理数字以外的数字变为0 
				if(r[j] == i)change[j] = 1;
			}
			int x = get(change);
			q.push(x);
			vis[x] = 1;
			while(!q.empty()){//bfs
				int now = q.front();
				q.pop();
				//cout << now << "\n";
				if(now == END){//搜索结束 
					if(step[now].size() < s.size()){
						ans = i;
						s = step[now];
						
					}
					else if(step[now].size() == s.size()){
						if(step[now]  < s){
							ans = i;
							s = step[now];
						}
					}
					//cout << ss << " " << ans << "\n";
					break;
				}
//				for(int j = 0; j < step[now].size(); j++){
//					cout << step[now][j];
//				}
//				cout << "\n";
				int y;
					y = shu(now,'A',0);//纵向向上移动第一列;y为变化后的十进制数 
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];
						step[y] += 'A';
					}
					
					y = shu(now,'B',0);//纵向向上移动第二列;
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];
						step[y] += 'B';
					}
					
					y = heng(now,'C',0);//横向向右移动第一行y为变化后的十进制数 
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];//复制步骤 
						step[y] += 'C';
					}
				
					y = heng(now,'D',0);//横向向右移动第二行 ,y为变化后的十进制数  
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];//复制步骤 
						step[y] += 'D';//添加当前进行的步骤 	
					}
					
					
					y = shu(now,'B',1);//纵向向下移动第二列,y为变化后的十进制数  
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];
						step[y] += 'E';
					}
					
					y = shu(now,'A',1);//纵向向下移动第一列,y为变化后的十进制数  
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];
						step[y] += 'F';
					}
					
					y = heng(now,'D',1);//横向向左移动第二行,y为变化后的十进制数  
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];//复制步骤 
						step[y] += 'G';//添加当前进行的步骤 	
					}
					
					y = heng(now,'C',1);//横向向左移动第一行,y为变化后的十进制数 
					if(!vis[y]){
						q.push(y);
						vis[y] = 1;
						for(int k = 0; k < step[now].size(); k++)step[y] += step[now][k];//复制步骤 
						step[y] += 'H';
					}
			}
		}
		if(s.size() == 0){
			printf("No moves needed\n");
		}
		else{
			cout << s << "\n";
			cout << ans << "\n";
		}
		
	}
}

IDA*

点击查看代码
#include<bits/stdc++.h>
using namespace std;
bool state[50][24];
int table[24],ans[50],res[50],maxd;
int moven[8][7]={
{0,2,6,11,15,20,22},
{1,3,8,12,17,21,23},
{10,9,8,7,6,5,4},
{19,18,17,16,15,14,13},
{23,21,17,12,8,3,1},
{22,20,15,11,6,2,0},
{13,14,15,16,17,18,19},
{4,5,6,7,8,9,10}
};//手动打移动规则
int mid[8]={6,7,8,11,12,15,16,17};//中间八个格子
int oppo[8]={5,4,7,6,1,0,3,2};//剪枝一
int f(int now)//估价函数
{
    int ans=0;
    for(int i=0;i<8;i++)if(!state[now][mid[i]])ans++;
    return ans;
}
bool dfs(int deep)//IDA*
{
    if(deep==maxd)
    {
        if(f(deep)==0)return 1;
        else return 0;
    }
    if(deep+f(deep)>maxd)return 0;
    for(int i=0;i<8;i++)
    if(deep==0||oppo[ans[deep-1]]!=i)
    {
        ans[deep]=i;
        for(int j=0;j<24;j++)state[deep+1][j]=state[deep][j];
        for(int j=0;j<7;j++)state[deep+1][moven[i][j]]=state[deep][moven[i][(j+1)%7]];
        if(dfs(deep+1))return 1;
    }
    return 0;
}
bool judge()//更新答案
{
    if(res[0]!=-1)
    for(int i=0;i<maxd;i++)
        if(res[i]<ans[i])return 0;
        else if(res[i]>ans[i])break;
    for(int i=0;i<maxd;i++)res[i]=ans[i];
    return 1;
}
int main()
{
    while(1)
    {
        memset(res,-1,sizeof(res));
        scanf("%d",&table[0]);
        if(!table[0])break;
        for(int i=1;i<=23;i++)scanf("%d",&table[i]);
        int p=0;
        for(maxd=0;;maxd++)
        {
            for(int k=1;k<=3;k++)//分三次求解
            {
                for(int j=0;j<24;j++)
                state[0][j]=table[j]==k;
                if(dfs(0)&&judge())p=k;//更新答案
            }
            if(p)break;
        }
        if(maxd)
        {
        	for(int i=0;i<maxd;i++)putchar(res[i]+'A');
        	putchar('\n');
        }
        else printf("No moves needed\n");
        printf("%d\n",p);
    }
    return 0;
}
最后,带宽uva140,困难的串uva129值得回看
posted @ 2022-06-28 11:11  腾云今天首飞了吗  阅读(40)  评论(0)    收藏  举报