HDOJ1054—— 树形DP & 二分图最小点覆盖
HDOJ1054—— 树形DP & 二分图最小点覆盖
问题描述
给定一个树形结构,我们可以在某些节点上安插士兵,使得与这个节点相连的边被覆盖到。求最少放多少个士兵,使得整张图的所有边都被覆盖到。
Sample Input
4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)
Sample Output
1
2
分析
解法一 树形DP
设DP[i][0] 为节点i不安插士兵的最小数量,dp[i][1]为节点i安插士兵的最小数量
易得状态转移方程
dp[i][0] = sum(dp[v][1]) v为i的子节点
dp[i][1] = sum(min(dp[v][0],dp[v][1]))
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1500 + 10;
vector<int> mat[maxn];
int n,dp[maxn][2];
void dfs(int root,int pre)
{
dp[root][0] = 0;
dp[root][1] = 1;
for(int i=0;i<mat[root].size();i++)
{
int v = mat[root][i];
if(v == pre) continue;
dfs(v,root);
dp[root][0] += dp[v][1];
dp[root][1] += min(dp[v][0],dp[v][1]);
}
}
int main()
{
int a,b,num;
while(cin >> n)
{
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
mat[i].clear();
for(int i=0;i<n;i++)
{
scanf("%d:(%d)",&a,&num);
for(int j=0;j<num;j++)
{
cin >> b;
mat[a].push_back(b);
mat[b].push_back(a);
}
}
dfs(0,-1);
cout << min(dp[0][0],dp[0][1]) << endl;
}
}
解法二 二分图最小点覆盖
可以将树的奇数层放在边,偶数层放在另一边,然后将相邻节点连边。问题就是求最小的节点数覆盖所有的边,即为这张二分图的最大匹配。
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn = 1500 + 10;
vector<int> boy,mat[maxn];
int n,cnt,vis[maxn],cur[maxn];
void color(int node,int c) //上色,将节点分成两个集合
{
if(c == 1) boy.push_back(node);
vis[node] = 1;
for(int i=0;i<mat[node].size();i++)
{
int v = mat[node][i];
if(vis[v]) continue;
color(v,-1 * c);
}
}
bool find(int node)
{
for(int i=0;i<mat[node].size();i++)
{
int v = mat[node][i];
if(!vis[v])
{
vis[v] = 1;
if(cur[v] == -1 || find(cur[v]))
{
cur[v] = node;
return true;
}
}
}
return false;
}
int main()
{
int a,b,num;
while(cin >> n)
{
memset(cur,-1,sizeof(cur));
boy.clear();
for(int i=0;i<=n;i++) mat[i].clear();
for(int i=0;i<n;i++)
{
scanf("%d:(%d)",&a,&num);
while(num--)
{
cin >> b;
mat[a].push_back(b);
mat[b].push_back(a);
}
}
memset(vis,0,sizeof(vis));
color(0,1);
cnt = 0;
for(int i=0;i<boy.size();i++)
{
memset(vis,0,sizeof(vis));
if(find(boy[i])) cnt++;
}
cout << cnt << endl;
}
}