2018期末上机汇总(1)

B08 迷宫入口  http://cxsjsx.openjudge.cn/2018ftall/B08/

一道搜索题,关键在于怎样表示一个状态,怎样设计放的顺序来剪枝。这里可以用一维数组来存放一个状态,表示map[i]表示第i列已经放的高度。然后为了保证第i列是连续的,我们假设每一次都选择最低的位置放,每一次放的边长都小于最低的那一段的长度。同时为了保证每一个状态只被搜索到一次,如果同时有多个最低的,就放最左边的那个。判断是否结束可以全部扫一遍,也可以先预处理一下碎片面积是不是和总面积一样,然后判断是不是所有碎片都用上了。

注释掉的地方还可以剪枝:如果当前最大的一块不能放进去,当前状态就不行了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string.h>
#include<string>
#define REP(i,a,b) for(int i=(a);i<=(b);++i)
#define PER(i,a,b) for(int i=(a);i>=(b);--i)
#define INF 0x7fffffff/2
#define MAX 100010
#define ll long long
using namespace std;
int l[20];//每一种边长有多少个
int s;
int n;
int map[410];
bool dfs(){
	int mi=INF;int st=0;int board=0;//int maxl=0,cnt=0;
	REP(i,1,s){
		if(map[i]<mi)mi=map[i];
		/*if(map[i]==map[i-1]||i==1)cnt++;
		else cnt=1;
		if(cnt>maxl){
			maxl=cnt;
		}*/
	}
	//int maxb=0;
	//PER(i,10,1)if(l[i]){maxb=i;break;}
	//if(maxb>maxl||maxb+mi>s)return 0;
	REP(i,1,s){
		if(map[i]==mi){
			st=i;int k=i;
			while(mi==map[k]&&k<=s){
				k++;board++;
			}
			break;
		}
	}
	REP(b,1,10){
		if(b<=board&&l[b]>0&&b+mi<=s){
			l[b]--;
			REP(k,st,st+b-1)map[k]+=b;
			REP(k,1,10){
				if(l[k])break;
				if(k==10&&!l[k])return 1;
			}
			if(dfs())return 1;
			l[b]++;
			REP(k,st,st+b-1)map[k]-=b;
		}
	}
	return 0;
}
int main(){
	int t;cin>>t;
	while(t--){
		cin>>s>>n;
		memset(l,0,sizeof(l));memset(map,0,sizeof(map));
		REP(i,1,n){
			int a;cin>>a;l[a]++;
			//cout<<a<<' '<<l[a]<<endl;
		}
		int sum=0;
		REP(i,1,10)sum+=l[i]*i*i;
		if(sum!=s*s){
			cout<<"NO"<<endl;continue;
		}
		if(dfs())cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yhjpku/article/details/80792362