题目链接。
题意:
给定一个树,选择若干点,使得选择的结点中任一结点不会和它的子结点同时选择,求能选结点最大数量。同时判断方案数是否为一。
分析:
如果单单求最大数量,很容易。之前也做过一个。链接:http://www.cnblogs.com/tanhehe/archive/2013/06/12/3132521.html
但是如何判断方案数是否唯一呢?
新加一个状态 dup[i][j],表示相应 dp[i][j] 是否唯一方案。
对于叶子结点,dup[k][0] = dup[k][1] = 1.
对于非叶子结点,
1.对于 i 的任意儿子 j, 若(dp[j][0] > dp[j][1] 且 dup[j][0] == 0) 或 (dp[j][0] < dp[j][1] 且 dup[j][1] == 0) 或 (dp[j][0] == dp[j][1]), 则 dup[i][0] = 0.
2.对于 i 的r任意儿子 j 有 dup[j][0] = 0, 则 dup[i][1] = 0。
在这里,左儿子右兄弟的写法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <map> using namespace std; const int maxn = 200 + 10; bool dup[maxn][2]; struct Tree { int child, brother, father; int Take, Not; void init() { child = brother = father = Not = 0; Take = 1; } }tree[maxn]; void dfs(int idx) { int child = tree[idx].child; while(child) { dfs(child); tree[idx].Take += tree[child].Not; tree[idx].Not += max(tree[child].Take, tree[child].Not); if((tree[child].Take > tree[child].Not && dup[child][1] == false) || (tree[child].Take < tree[child].Not && dup[child][0] == false) || (tree[child].Take == tree[child].Not)) dup[idx][0] = false; if(dup[child][0] == false) dup[idx][1] = false; child = tree[child].brother; } } int main() { int n; char s1[100+10], s2[100+10]; while(scanf("%d", &n) == 1 && n) { int cnt = 0; map<string, int> a; for(int i=1; i<=n; i++) { tree[i].init(); } scanf("%s", s1); a[s1] = ++cnt; for(int i=2; i<=n; i++) { scanf("%s%s", s1, s2); if(a.count(s1) == 0) a[s1] = ++cnt; int d1 = a[s1]; if(a.count(s2) == 0) a[s2] = ++cnt; int d2 = a[s2]; tree[d1].father = d2; tree[d1].brother = tree[d2].child; tree[d2].child = d1; } memset(dup, true, sizeof(dup)); dfs(1); printf("%d ", max(tree[1].Take, tree[1].Not)); if((tree[1].Take > tree[1].Not && dup[1][1] == false) || (tree[1].Take < tree[1].Not && dup[1][0] == false) || tree[1].Take == tree[1].Not) printf("No\n"); else printf("Yes\n"); } }
或者直接建树,写法差不多:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <map> using namespace std; const int maxn = 200 + 10; struct Node { int from, to, next; }edges[maxn]; int n, mem, head[maxn]; int dp[maxn][2]; bool dup[maxn][2]; void init() { mem = 0; memset(head, -1, sizeof(head)); } void AddEdge(int u, int v) { edges[mem].from = u; edges[mem].to = v; edges[mem].next = head[u]; head[u] = mem++; } void dfs(int idx) { dp[idx][0] = 0; dp[idx][1] = 1; dup[idx][0] = dup[idx][1] = true; for(int i=head[idx]; i != -1; i = edges[i].next) { int v = edges[i].to; dfs(v); dp[idx][0] += max(dp[v][0], dp[v][1]); dp[idx][1] += dp[v][0]; if((dp[v][0] > dp[v][1] && !dup[v][0]) || (dp[v][0] < dp[v][1] && !dup[v][1]) || dp[v][0] == dp[v][1]) dup[idx][0] = false; if(!dup[v][0]) dup[idx][1] = false; } } int main() { char s1[100+10], s2[100+10]; while(scanf("%d", &n) == 1 && n) { map<string, int> a; init(); int cnt = 0; scanf("%s", s1); a[s1] = ++cnt; for(int i=1; i<n; i++) { scanf("%s %s", s1, s2); if(a.count(s1) == 0) a[s1] = ++cnt; if(a.count(s2) == 0) a[s2] = ++cnt; AddEdge(a[s2], a[s1]); } dfs(1); printf("%d ", max(dp[1][0], dp[1][1])); if((dp[1][1] > dp[1][0] && !dup[1][1]) || (dp[1][1] < dp[1][0] && !dup[1][0]) || dp[1][0] == dp[1][1]) printf("No\n"); else printf("Yes\n"); } }
分析参考:http://wenku.baidu.com/view/aee323cd0508763231121252.html(第8页)