CF 429C
中文题意:
给出一棵树中每个节点子树的大小,要求每个非叶节点至少有2个儿子,问是否能构造出这样一棵树
解法:
N<=24,因此可以用状压解。
首先可以证明如果叶节点<N/2,那么肯定无解
所以我们可以把非叶节点和叶节点分开处理。
我们每次其实就是要往一个非叶点里塞点,或者说给每个点找父节点
我们设状态 f[i][j][k]为已塞完前i个非叶节点,非叶节点是否被塞过(即是否已有父节点的状态为j,还有k个叶节点没有父节点
然后对于每个点,枚举塞哪些非叶节点即可
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> using namespace std; bool f[14][8201][14]; int zt[9011],num[9011]; int a[101]; int n,i,j,k,cnt,t; void Work(int x,int y,int z) { int lef,i,yf; for(i=0;i<(1<<t);i++)if(!(y&i)){ lef=a[x]-1-zt[i]; if(lef>=0&&lef<=z){ if(lef+num[i]>1)f[x][y|i][z-lef]=true; } } } int main() { scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&k); if(k==1)cnt++; else a[++t]=k; } for(i=1;i<t;i++) for(j=i+1;j<=t;j++)if(a[i]<a[j])swap(a[i],a[j]); if(n==1&&cnt==1)printf("YES\n"); else if(cnt<n/2||cnt==n)printf("NO\n"); else{ for(i=0;i<(1<<t);i++){ for(j=1;j<=t;j++)if((i&(1<<(j-1)))){ zt[i]+=a[j]; num[i]++; } } f[0][1][cnt]=true; for(i=0;i<t;i++) for(j=0;j<(1<<t);j++) for(k=0;k<=cnt;k++)if(f[i][j][k])Work(i+1,j,k); if(f[t][(1<<t)-1][0])printf("YES\n"); else printf("NO\n"); } }