P2458 [SDOI2006]保安站岗
没学树形DP的,看一下。
题目大意:一棵树有N个节点,现在需要将所有节点都看守住,如果我们选择了节点i,那么节点i本身,节点i的父亲和儿子都会被看守住。
每个节点有一个选择代价,求完成任务所需要的最小的代价。
分析:根据每个节点其实有只有三个状态:
①被自己看守;②被儿子看守;③被父亲看守。
我们设这三种状态分别为F1,F2,F3。
当然最终作为答案的根节点没有父亲就没有F3。
接下来我们要考虑怎么转移。
首先看F1,我们规定F1[ i ]代表的是i节点被自己看守且以i为根的子树都已被看守的最小代价,也就是说一定会选择 i 节点自己,答案中必定会加入选择他自己的代价Wi。
因为这个点会被自己看管,所以只要考虑在其儿子的三个状态中选一个最小的,保证这个节点下面的子树都已被看守就行了。
所以F1[ i ] += min( F1[ Si ], F2[ Si ], F3[ Si ] ) + w[ i ],其中Si代表i节点的儿子。
接下来看F2,我们规定F2[ i ]代表i节点被儿子看守且以i为根的子树都已被看守的最小代价,也就是说一定不选i节点,但是至少要在i节点的儿子中选择一个而且最多也就选一个,因为代
价是正数,选一个就能把i看住,就不需要选择多余的点在增加代价了。
因为i节点不能被选,所以只能在其儿子的F1, F2状态中选择小的(F3[ Si ]代表选择i节点,而不能选i节点,所以不能用F3[ Si ]),来保证其子树都已被看守。
所以F2[ i ] += min{ F1[ Si ], F2[ Si ] } + t。t代表选择一个儿子的最小代价:t = F1[ Si ] - min{ F1[ Si ], F2[ Si ] }。
顺便解释一下t的转移:t是Si被看管的代价中选一个最小的,如果是F1,那么说明Si已经被选,就不用再加W[ Si ]了,如果是F2,那么F1 - F2 = W[ i ]。(注意F1和F2代表的意义)
最后看F3,我们规定F3[ i ]代表i节点被父亲看守且以i为根的子树都已经被看守的最小代价,也就是说一定不选i节点和其儿子节点,必须选择他的父亲。因为必须选择父亲,那么i一定会被父亲看守,那么我们只要保证其下面的子树都已被看守,就是在儿子的F1, F2中选一个小的,因为还是不能选i,所以其儿子的F3状态仍然不用考虑,同F2。
所以F3[ i ] += min{ F1[ Si ], F2[ Si ]}。
看代码吧……………*&%^qaq^%&*
#include#include #include using namespace std; const int maxn=1505; const int inf=0x3f3f3f3f; int n; struct edge{ int num,k,m; }e[maxn]; int s[maxn][maxn],fa[maxn],f1[maxn],f2[maxn],f3[maxn]; int ans; void tree_dp(int i) { f1[i]=e[i].k; f2[i]=f3[i]=0; int minn=inf; for (int j=1;j<=e[i].m;j++) { tree_dp(s[i][j]); f1[i]+=min(f1[s[i][j]],min(f2[s[i][j]],f3[s[i][j]])); f2[i]+=min(f1[s[i][j]],f2[s[i][j]]); int t=f1[s[i][j]]-min(f1[s[i][j]],f2[s[i][j]]); minn=min(minn,t); f3[i]+=min(f1[s[i][j]],f2[s[i][j]]); } f2[i]+=minn; } void work() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&e[i].num); //注意读入 scanf("%d%d",&e[e[i].num].k,&e[e[i].num].m); for (int j=1;j<=e[e[i].num].m;j++) { scanf("%d",&s[e[i].num][j]); //儿子节点数 fa[s[e[i].num][j]]=e[i].num; } } memset(f1,inf,sizeof(f1)); memset(f2,inf,sizeof(f2)); memset(f3,inf,sizeof(f3)); for (int i=1;i<=n;i++) { if(!fa[i]) //没有父亲,就是根节点 { tree_dp(i); ans=min(f1[i],f2[i]); //根节点只有2种情况 break; } } printf("%d",ans); } int main() { work(); return 0; }