解题思路
直接爆搜全T。。状态数太多了,所以我们考虑贪心+剪枝。贪心:先拿三个连着的,再拿四个一样的,再拿三个一样的,最后拿两个一样的这样的搜索顺序最优,两个的放最后是因为只要这样的一个,三个连着的放开头是因为这样可以照顾到后面很少的情况。这样肯定还是T,我们考虑剪枝,用hash减。考虑如果数据为
12 0 1 0 0 0…. 这样的 ,我们第一种搜的方案是 a[1]里拿三个4,后面发现不行,返回。第二种方案是a[1]拿四个3,发现这两种方案拿完a[1]后 剩下的序列都是0 1 0 0…,所以将剩下的序列hash一下存到set里,如果再次遇到就直接返回false,注意这里直接去模会T的很惨,所以要用到unsigned long long 里的自然溢出。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#define ULL unsigned long long
using namespace std;
const int MAXN = 105;
const ULL base = 131ll;
int n,a[MAXN];
ULL hash[MAXN],sum;
set<ULL> s;
bool dfs(int x,bool two,ULL S){
if(s.find(S)!=s.end()) return 0;
s.insert(S);
while(a[x]==0 && x<=100) x++;
if(x==101) return two;
if(a[x]>0 && a[x+1]>0 && a[x+2]>0) {
a[x]--;a[x+1]--;a[x+2]--;
if(dfs(x,two,S-hash[x]-hash[x+1]-hash[x+2])) return 1;
a[x]++;a[x+1]++;a[x+2]++;
}
if(a[x]>=4) {
a[x]-=4;
if(dfs(x,two,S-hash[x]*4)) return 1;
a[x]+=4;
}
if(a[x]>=3){
a[x]-=3;
if(dfs(x,two,S-hash[x]*3)) return 1;
a[x]+=3;
}
if(a[x]>=2 && !two){
a[x]-=2;
if(dfs(x,1,S-hash[x]*2-hash[100])) return 1;
a[x]+=2;
}return 0;
}
int main(){
// freopen("rand.txt","r",stdin);
// freopen("B.txt","w",stdout);
scanf("%d",&n);
hash[1]=1ll;
for(register int i=2;i<=100;i++) {
hash[i]=hash[i-1]*base;
}
for(register int i=1;i<=n;i++){
sum=0;
for(register int j=1;j<=100;j++){
scanf("%d",&a[j]);sum+=hash[j]*a[j];
}
s.clear();
puts(dfs(1,0,sum)==true?"Yes":"No");
}
return 0;
}