题目的意思是给你一个树求一个最小的覆盖,有一种做法是把树结构转换为二部图,树结构是一定能转换为二部图结构的,因为树没有 “圈”结构(起点和终点相,但是中间的点都不同) ,所以可以把树的节点染色,一个节点染成白色,和这个节点相邻的节点就染成黑色。再把黑色和白色分成两部分,这样就能构造一个二部图结构!
另外,对于任意一个图,
能转换为二部图结构 <==> 在这个图中不存在长度为奇数的圈
可以证明他们是充要条件
由于在任意的二部图中最小覆盖集中的元素个数和最大匹配的个数是相同的(这个也可以证明),所以可以把求最小覆盖的问题再次转换为求一个最大匹配的问题!而最大匹配的话则可以选择网络流算法,匈牙利算法,或者是贪心算法。
不过我们还可以用树状dp来解决这个问题,树结构有四个充要的性质:
(1)任何 n 阶树都有 n - 1条边。
(2)任意两个节点有且只有一条链,且这条链的长度就是这两点的距离。
(3)树中没有“圈”。
(4)树中的每一条边都是桥。
利用这4个性质,也就有了树状dp。
随便选一个点作为根节点,在根节点可以选择放还是不放,如果不放,孩子节点就都要放;如果放,孩子节点可放可不放。然后利用一个深度优先搜索或广度优先搜索来遍历一遍,就能得到最后的答案!
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<string>
#include<vector>
#include<cstdio>
#include<cctype>
#include<cmath>
#include<queue>
#include<stack>
#include<deque>
#include<cmath>
#include<list>
#include<set>
using namespace std;
const int inf=0x7fffffff;
const int maxn=2222;
struct zz
{
int y;
int n;
}dp[maxn];
int N,m,vx,t2,temp;
string cs;
vector<int>g[maxn];
bool vis[maxn];
inline void give()
{
temp=1;
m=0;
vx=cs[0]-'0';
for(int i=1;i<cs.length();i++)
{
if(cs[i]<='9' && cs[i]>='0')
{
if(temp)
{
vx*=10;
vx+=cs[i]-'0';
}
else
{
m*=10;
m+=cs[i]-'0';
}
}
else
{
i++;
temp=0;
continue;
}
}
return;
}
void dfs_dp (int root=0)
{
vis[root]=true;
int tyes=1;
int tno=0;
for(int i=0;i<g[root].size();i++)
{
if(vis[g[root][i]])
{
continue;
}
dfs_dp ( g[root][i] );
tyes+=min(dp[g[root][i]].y,dp[g[root][i]].n);
tno+=dp[g[root][i]].y;
}
dp[root].y=tyes;
dp[root].n=tno;
return;
}
void dp_start ()
{
memset(vis,0,sizeof(vis));
dfs_dp ();
cout<<min(dp[0].y,dp[0].n)<<endl;
return;
}
int main()
{
while(cin>>N)
{
for(int i=1;i<=N;i++)
{
g[i].clear();
}
for(int i=1;i<=N;i++)
{
cin>>cs;
give();
for(int j=1;j<=m;j++)
{
cin>>t2;
g[vx].push_back(t2);
g[t2].push_back(vx);
}
}
dp_start();
}
return 0;
}