Vijos p1770 大内密探 树形DP+计数
4天终于做出来了,没错我就是这么蒟蒻。教训还是很多的。
建议大家以后编树形DP不要用记忆化搜索,回溯转移状态个人感觉更有条理性。
大神题解传送门 by iwtwiioi
我的题解大家可以看注释"//"部分
本题我用的树形DP中dp[x][fa][need]表示编号为x的节点的父亲选(1)没选(0),x的父亲需(1)不需要(0)其他节点来覆盖。
若父亲节点选了,则need肯定为0,所以不存在fa==1而need==1的状态,相当于浪费了¼的空间。毕竟数据范围比较小,而且程序要有可读性!程序要有可读性!程序要有可读性!我这么写代码不是加强了程序的可读性吗?像iwtwiioi那样用012表示状态题解和程序的可读性大大降低,大家在读题解时难免要费点劲。并没有贬义!!!╮(╯_╰)╭
my code:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int point[100003],next[200003],v[200003],cnt=0,N,l[100003],r[100003],pre[100003],con[100003]; const long long mo=1000000007; long long f[100003][2][2]; int dp[100003][2][2]; bool p[100003]; void memsetf(){for(int i=0;i<=N;++i)for(int j=0;j<2;++j)for (int k=0;k<2;++k)f[i][j][k]=1;} void insect(int x,int y){next[cnt]=point[x];point[x]=cnt;v[cnt]=y;cnt++;} void dfs(int x) { if (x==0) return; int i,j=0; for (i=point[x];i!=-1;i=next[i]) if (p[v[i]]==0) { p[v[i]]=1; if (l[x]==0) {l[x]=v[i];j=v[i];} else {r[j]=v[i];pre[v[i]]=j;j=v[i];} } dfs(j); con[j]=con[l[j]]+1; while (pre[j]!=-1){j=pre[j]; dfs(j); con[j]=con[l[j]]+con[r[j]]+1;} } void work(int x) { if (r[x]!=0) work(r[x]); if (l[x]!=0) work(l[x]); long long s=0; int num=1000000008; if ((l[x]==0)&&(r[x]==0)) { dp[x][0][1]=1; dp[x][0][0]=1; dp[x][1][0]=0; return; } else if (l[x]==0) { dp[x][0][1]=1+dp[r[x]][0][0]; f[x][0][1]=f[r[x]][0][0]; dp[x][0][0]=1+dp[r[x]][0][0]; f[x][0][0]=f[r[x]][0][0]; dp[x][1][0]=dp[r[x]][1][0]; f[x][1][0]=f[r[x]][1][0]; return; } else if (r[x]==0) { dp[x][0][1]=1+dp[l[x]][1][0]; f[x][0][1]=f[l[x]][1][0]; //dp[x][0][0]=min(1+dp[l[x]][1][0],dp[l[x]][0][1]); if (1+dp[l[x]][1][0]<dp[l[x]][0][1]) dp[x][0][0]=1+dp[l[x]][1][0],f[x][0][0]=f[l[x]][1][0]; else if (1+dp[l[x]][1][0]>dp[l[x]][0][1]) dp[x][0][0]=dp[l[x]][0][1],f[x][0][0]=f[l[x]][0][1]; else dp[x][0][0]=dp[l[x]][0][1],f[x][0][0]=(f[l[x]][1][0]+f[l[x]][0][1])%mo; //dp[x][1][0]=min(1+dp[l[x]][1][0],dp[l[x]][0][0]); if (1+dp[l[x]][1][0]<dp[l[x]][0][0]) dp[x][1][0]=1+dp[l[x]][1][0],f[x][1][0]=f[l[x]][1][0]; else if (1+dp[l[x]][1][0]>dp[l[x]][0][0]) dp[x][1][0]=dp[l[x]][0][0],f[x][1][0]=f[l[x]][0][0]; else dp[x][1][0]=dp[l[x]][0][0],f[x][1][0]=(f[l[x]][1][0]+f[l[x]][0][0])%mo; return; } //dp[x][0][1]=min(dp[x][0][1],dp[l[x]][0][1]+dp[r[x]][0][1]); //dp[x][0][1]=min(dp[x][0][1],1+dp[l[x]][1][0]+dp[r[x]][0][0]); if (dp[l[x]][0][1]+dp[r[x]][0][1]<1+dp[l[x]][1][0]+dp[r[x]][0][0]) dp[x][0][1]=dp[l[x]][0][1]+dp[r[x]][0][1],f[x][0][1]=(f[l[x]][0][1]*f[r[x]][0][1])%mo; else if (dp[l[x]][0][1]+dp[r[x]][0][1]>1+dp[l[x]][1][0]+dp[r[x]][0][0]) dp[x][0][1]=1+dp[l[x]][1][0]+dp[r[x]][0][0],f[x][0][1]=(f[l[x]][1][0]*f[r[x]][0][0])%mo; else { dp[x][0][1]=1+dp[l[x]][1][0]+dp[r[x]][0][0]; f[x][0][1]=((f[l[x]][0][1]*f[r[x]][0][1])%mo+(f[l[x]][1][0]*f[r[x]][0][0])%mo)%mo; } //dp[x][0][0]=min(dp[x][0][0],dp[l[x]][0][1]+dp[r[x]][0][0]); //dp[x][0][0]=min(dp[x][0][0],1+dp[l[x]][1][0]+dp[r[x]][0][0]); if (dp[l[x]][0][1]+dp[r[x]][0][0]<1+dp[l[x]][1][0]+dp[r[x]][0][0]) dp[x][0][0]=dp[l[x]][0][1]+dp[r[x]][0][0],f[x][0][0]=(f[l[x]][0][1]*f[r[x]][0][0])%mo; else if (dp[l[x]][0][1]+dp[r[x]][0][0]>1+dp[l[x]][1][0]+dp[r[x]][0][0]) dp[x][0][0]=1+dp[l[x]][1][0]+dp[r[x]][0][0],f[x][0][0]=(f[l[x]][1][0]*f[r[x]][0][0])%mo; else { dp[x][0][0]=1+dp[l[x]][1][0]+dp[r[x]][0][0]; f[x][0][0]=((f[l[x]][1][0]*f[r[x]][0][0])%mo+(f[l[x]][0][1]*f[r[x]][0][0])%mo)%mo; } //dp[x][1][0]=min(dp[x][1][0],dp[l[x]][0][0]+dp[r[x]][1][0]); //dp[x][1][0]=min(dp[x][1][0],1+dp[l[x]][1][0]+dp[r[x]][1][0]); if (dp[l[x]][0][0]+dp[r[x]][1][0]<1+dp[l[x]][1][0]+dp[r[x]][1][0]) dp[x][1][0]=dp[l[x]][0][0]+dp[r[x]][1][0],f[x][1][0]=(f[l[x]][0][0]*(f[r[x]][1][0]))%mo; else if (dp[l[x]][0][0]+dp[r[x]][1][0]>1+dp[l[x]][1][0]+dp[r[x]][1][0]) dp[x][1][0]=1+dp[l[x]][1][0]+dp[r[x]][1][0],f[x][1][0]=(f[l[x]][1][0]*f[r[x]][1][0])%mo; else { dp[x][1][0]=1+dp[l[x]][1][0]+dp[r[x]][1][0]; f[x][1][0]=((f[l[x]][0][0]*(f[r[x]][1][0]))%mo+(f[l[x]][1][0]*f[r[x]][1][0])%mo)%mo; } } int main() { memset(point,-1,sizeof(point)); memset(next,-1,sizeof(next)); memset(pre,-1,sizeof(pre)); memset(dp,50,sizeof(dp)); memset(v,0,sizeof(v)); memset(p,0,sizeof(p)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); scanf("%d\n",&N); memsetf(); int i,x,y; for (i=1;i<N;++i) { scanf("%d %d\n",&x,&y); insect(x,y);insect(y,x); }p[1]=1;dfs(1); work(l[1]); if (dp[l[1]][0][1]<(1+dp[l[1]][1][0])) printf("%d\n%I64d\n",dp[l[1]][0][1],f[l[1]][0][1]); else if (dp[l[1]][0][1]>(1+dp[l[1]][1][0])) printf("%d\n%I64d\n",1+dp[l[1]][1][0],f[l[1]][1][0]); else printf("%d\n%I64d\n",1+dp[l[1]][1][0],(f[l[1]][0][1]+f[l[1]][1][0])%mo); return 0; }
背景
大内密探,负责秘密保护皇上,还有保护皇宫内外一切产业。——大内密探零零七
描述
在古老的皇宫中,有N个房间以及N-1条双向通道,每条通道连接着两个不同的房间,所有的房间都能互相到达。皇宫中有许多的宝物,所以需要若干个大内密探来守护。一个房间被守护当切仅当该房间内有一名大内密探或者与该房间直接相邻的房间内有大内密探。
现在身为大内密探零零七的你想知道要把整个皇宫守护好至少需要多少名大内密探以及有多少种安排密探的方案。两种方案不同当且仅当某个房间在一种方案有密探而在另一个方案内没有密探。
格式
输入格式
第一行一个正整数N.(1<=N<=100000)
后面N-1行,每行两个正整数a和b,表示房间a和房间b之间有一条无向通道。
房间的编号从1到N
输出格式
第一行输出正整数K,表示最少安排的大内密探。
第二行输出整数S,表示有多少种方案安排最少的密探,由于结果可能较大,请输出方案数mod 1000000007的余数。
限制
每个测试点1s
提示
30%保证:n <= 10
70%保证:n <= 1000
100%保证:n <= 100000