NOIP2004 虫食算
题面https://www.luogu.org/problemnew/show/P1092
肯定要从右到左,从上到下比较好处理。
必然记录一个数用过与否,某个字母是什么。
剪枝:
1.到了第三行,上面两个已经确定,可以判断。
2.往后预估。但是进位怎么考虑??
没有关系!进位最多一位!!
如果对于已经填好2个的3元组,可以推出另外一个的2种可能情况,如果都被占据,return false
对于3个都填好了。也可以这样判断。
(官方正解是高斯消元,但是我认为复杂度不对,因为数据水,好像没有21以上的n)
代码:
(数据的原因,倒序枚举更快)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=28; char mp[4][N]; int is[N]; bool has[N]; int ans[N]; int n; int num[4][N]; bool fl; int lp(int x,int y){ return mp[x][y]-'A'; } bool che(int st){ //return true; int jin=0;//return true; for(int j=st;j>=1;j--){ int a=is[lp(1,j)],b=is[lp(2,j)],c=is[lp(3,j)]; if(a!=-1&&b!=-1&&c!=-1){ if(c!=(a+b)%n&&c!=(a+b+1)%n) return false; } else if(a!=-1&&b!=-1&&c==-1){ if(has[(a+b)%n]&&has[(a+b+1)%n]) return false; } else if(a!=-1&&b==-1&&c!=-1){ if(has[((c-a-1)+n)%n]&&has[((c-a)+n)%n]) return false; } else if(a==-1&&b!=-1&&c!=-1){ if(has[((c-b-1)+n)%n]&&has[((c-b)+n)%n]) return false; } } return true; } void dfs(int x,int y,int jin){ //cout<<x<<" and "<<y<<endl; if(fl) return; if(x==1&&y==0){ if(jin) return; fl=true; memcpy(ans,is,sizeof is);return; } if(!che(y)) { return; } if(x==3){ int a=is[mp[1][y]-'A']; int b=is[mp[2][y]-'A']; int tmp=a+b+jin; int to=tmp%n; if(is[mp[x][y]-'A']==-1){ if(has[to]) return; has[to]=1; is[mp[x][y]-'A']=to; num[x][y]=to; dfs(1,y-1,tmp/n); has[to]=0; is[mp[x][y]-'A']=-1; num[x][y]=-1; } else{ if(is[lp(x,y)]!=to) return; dfs(1,y-1,tmp/n); } } else{ if(is[mp[x][y]-'A']==-1) for(int i=n-1;i>=0;i--){ if(!has[i]){ has[i]=1; is[mp[x][y]-'A']=i; num[x][y]=i; dfs(x+1,y,jin); has[i]=0; is[mp[x][y]-'A']=-1; num[x][y]=-1; } } else { num[x][y]=is[lp(x,y)]; dfs(x+1,y,jin); num[x][y]=-1; } } } int main(){ scanf("%d",&n); fl=false; for(int i=1;i<=3;i++)scanf("%s",mp[i]+1); memset(is,-1,sizeof is); memset(num,-1,sizeof num); dfs(1,n,0); //cout<<fl<<endl; for(int i=0;i<n;i++){ printf("%d ",ans[i]); }return 0; }
总结:
往后预估不一定要很准确。
毕竟剪枝是玄学的。
可以稍微放宽一些,反而更加有效。