从放第一本书开始推,每次放两本相同高度的,设$f_{i,j}$为左边放i本书,右边放j本书的方案,放书的时候判断一下是否合法,如果合法,$f_{i,j}$+=$f_{i-2,j}$,$f_{i-1,j-1}$,$f_{i,j-2}$ ,这个方程应该不难理解
至于条件可以用邻接表,注意一下邻接表的数据范围,因为书的位置有200个,正反都存一遍就好了
符号的正反:注意与 >= 相对的是 <= , 不是 <
这是我用了 N(N>20) 次调试换来的······
得到的结果要除以3,因为当放最后两本书时,$f_{i-2,j}$==$f_{i-1,j-1}$==$f_{i,j-2}$
上代码:
#include<bits/stdc++.h> using namespace std; long long n,f[210][210],tot,k,ans; long long head[210],next11[410],ver[410],edge[410]; //一定开long long,一定开long long,一定开long long void add(long long x,long long y,long long z){ next11[++tot]=head[x],ver[tot]=y,edge[tot]=z,head[x]=tot; }//邻接表 bool spfa(long long l,long long r,long long ll,long long rr){ //寻找出边,l表示放的两本书中左边的那本,r是右边那本,ll表示没有放书的区间的左端点,rr表示右端点,可知 ll~rr 中的书高度都大于将要放的两本书 for(int i=head[l];i;i=next11[i]) { int y=ver[i],z=edge[i]; if(y==r){ if(z==2||z==4) return false; }//特判,等于的情况只可能:出边为放的另一本书的位置 else if(z<=2) { if(y<ll||y>rr) return false; } else if(z>=4) { if(y>ll&&y<rr) return false; } else if(z==3) return false; }//寻找与左端点有关的条件 for(long long i=head[r];i;i=next11[i]) { long long y=ver[i],z=edge[i]; if(y==l){ if(z==2||z==4) return false; } else if(z<=2) { if(y<ll||y>rr) return false; } else if(z>=4) { if(y>ll&&y<rr) return false; } else if(z==3) return false; }//同上 return true; } int main(){ cin>>n>>k; for(int i=1;i<=k;i++) { long long x,y,z=0; string c; cin>>x>>c>>y; if(c[1]=='=') { if(c[0]=='<') z=1; else z=5; } else { if(c[0]=='=') z=3; if(c[0]=='<') z=2; if(c[0]=='>') z=4; } if(x==y) { if(z==2||z==4) { cout<<0; return 0; } else continue; } add(x,y,z); add(y,x,6-z);//懂我为什么强调了吧 } f[0][0]=1; for(int i=1;i<=n;i++) { for(int j=0;j<=2*i;j++) { if(j>=2&&(k==0||spfa(j-1 , j , j+1 , 2*n-2*i+j))) f[j][2*i-j]+=f[j-2][2*i-j]; if(j>=1&&2*i-j>=1&&(k==0||spfa( j , 2*n-2*i+j+1 , j+1 , 2*n-2*i+j))) f[j][2*i-j]+=f[j-1][2*i-j-1]; if(2*i-j>=2&&(k==0||spfa(2*n-2*i+j+1 , 2*n-2*i+j+2 , j+1 , 2*n-2*i+j))) f[j][2*i-j]+=f[j][2*i-j-2]; }//自行理解,最好画个图 } for(int i=0;i<=2*n;i++) ans+=f[i][2*n-i];//ans累加, cout<<ans/3;//重要 return 0; }