题目链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3659
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define inf (1<<30)
// next[i]代表节点i的子节点
vector<int> next[10010];
// adj[i]代表节点i的相邻节点
vector<int> adj[10010];
// visit[i] = 1/0代表该点是否被遍历过
int visit[10010];
// record[i][0/1]代表以i为根不选/选i的最小完美点覆盖的点个数
int record[10010][2];
int get_min(int x, int f);
void dfs_search(int x);
int n;
int main()
{
while(1)
{
scanf("%d", &n);
memset(record, -1, sizeof(record));
memset(visit, 0, sizeof(visit));
for(int i = 1; i <= n; i++)
{
next[i] = vector<int>();
adj[i] = vector<int>();
}
// 读入情况
for(int i = 1; i <= n-1; i++)
{
int x, y;
scanf("%d %d", &x, &y);
adj[x].push_back(y);
adj[y].push_back(x);
}
// 建立树
dfs_search(1);
/*
for(int i = 1; i <= n; i++)
{
printf("%d: ", i);
for(int j = 0; j < next[i].size(); j++)
printf("%d ", next[i][j]);
printf("\n");
}
*/ // 计算结果
printf("%d\n", min(get_min(1, 0), get_min(1, 1)));
int x;
scanf("%d", &x);
if(x == -1)
break;
}
return 0;
}
// 建立树
void dfs_search(int x)
{
visit[x] = 1;
for(int i = 0; i < adj[x].size(); i++)
{
if(visit[adj[x][i]] == 0)
{
next[x].push_back(adj[x][i]);
dfs_search(adj[x][i]);
}
}
}
// 计算结果record[x][f];
int get_min(int x, int f)
{
if(record[x][f] != -1)
return record[x][f];
// 如果是叶子节点
if(next[x].size() == 0)
{
// 如果不选该点,那么最小覆盖点个数为无穷大
if(f == 0)
record[x][f] = inf;
// 选该点,最小覆盖点个数为1
else
record[x][f] = 1;
return record[x][f];
}
// 如果不是叶子节点
// 如果不选该点, 需要从子节点中选一个,其他子节点不选
if(f == 0)
{
int not_sum = 0;
// 统计子节点均不选的总数
int must_count = 0;
int must_num = 0;
for(int i = 0; i < next[x].size(); i++)
{
int val = get_min(next[x][i], 0);
if(val == inf)
{
must_count++;
must_num = next[x][i];
}
else
not_sum += val;
}
// 如果必须选的子节点数超过1个,那么该点的最小覆盖为不可能,无穷大
if(must_count >= 2)
{
record[x][f] = inf;
}
// 如果必须选的子节点为1个,如果可选就选该子节点,其他子节点不选
else if(must_count == 1)
{
int val = get_min(must_num,1);
if(val == inf)
record[x][f] = inf;
else
record[x][f] = not_sum + val;
}
// 如果没有必须选的子节点,就依次尝试可以选的子节点
else
{
int min_num = inf;
for(int i = 0; i < next[x].size(); i++)
{
int val = get_min(next[x][i], 1);
if(val != inf)
{
int t_num = not_sum - get_min(next[x][i],0) + val;
if(min_num > t_num)
min_num = t_num;
}
}
record[x][f] = min_num;
}
}
// 如果选该节点,那么针对每个儿子,选择(选儿子,不选儿子也不选孙子)的较小情况
else
{
int sum = 1;
for(int i = 0; i < next[x].size(); i++)
{
int v1 = get_min(next[x][i], 1);
int s = next[x][i];
int v2 = 0;
for(int j = 0; j < next[s].size(); j++)
{
int this_v = get_min(next[s][j], 0);
if(this_v == inf)
{
v2 = inf;
break;
}
else
v2 += this_v;
}
if(v1 == inf && v2 == inf)
{
sum = inf;
break;
}
else
{
sum += min(v1, v2);
}
}
record[x][f] = sum;
}
return record[x][f];
}
这题类似于点覆盖,但又不一样,不妨叫完美覆盖。
需要2维状态来表示每个点较为方便
d(i, 0): 以i为根,不选i的完美覆盖的点个数
d(i, 1): 以i为根,选i的完美覆盖的点个数
针对每个叶子节点:d(i, 1) = 1, d(i, 0) = inf (inf 为无穷大)
针对非叶子节点:
d(i, 0) = min( d(j,1) + sum(d(k,0) (k != j)) (k, j均为i的儿子)) 。
不选i时,需要选定i的一个儿子,其他儿子则必须不能选。
d(i, 1) = sum(min( d(j, 1) , d(k,0) (k为j的儿子))) (j为i的儿子)
选i时,针对i的每个儿子j有两种情况:(1).选该儿子j (2).不选该儿子j,此时节点j的所有儿子也不能选。 这两种情况选择较小的那一种。
769

被折叠的 条评论
为什么被折叠?



