题目:click
问最小需要多少点与树中所有点都有联系。
最小支配集:对于图G = <v, e> 来说,最小支配集指的是从 v 中取最少的点组成一个集合,使得 V 中剩余的点都与取出来的点有边相连。设 v0是图的一个支配集,则对于图中的任意一个顶点 u ,要么属于集合 v0, 要么与 v0 中的顶点相邻. 在 v0中除去任何元素后v0 不再是支配集, 则支配集 v0 是极小支配集。
同理最小点覆盖的话,就是包揽的全部的边:
(这里附上例题:poj-1463)最小点覆盖。也就只有两状态具体见最后贴出代码。
用最小支配集去做的话建立状态:
dp[i][0]:表示不是支配集中的点,他的父节点是支配集中的点。
dp[i][1]:表示该点是支配集。
dp[i][2]:表示不是支配集中的点,有子节点是。
(直接用图的最小支配集贪心也是可以)
题目中已经已经申明为树相互联系。
直接树形dp可建立状态转移方程:
dp[i][0]:表示该节点的父亲节点已经染色,该点可染色或者不染色。
dp[i][1]:表示该点直接染色,那么子节点和父节点可选择染色或者不染色。
dp[i][2]:表示该点的子节点染色,可是子节点很多我需要去确定一个最优的子节点。(该点为子节点的父节点确定不染色,所以有两种情况去判断)
见代码:
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<map>
#include<algorithm>
#include<queue>
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
int n;
vector<int>hh[10011];
int dp[10011][3];
bool vis[10011];
void dfs(int root,int f)
{
if(root!=1&&hh[root].size()==1)
{
dp[root][2]=1e9+9;
dp[root][0]=0;
dp[root][1]=1;
return ;
}
dp[root][1]=1;
int MIN=1e9+10;
bool flag=false;
for(int i=0;i<hh[root].size();i++)
{
int v=hh[root][i];
if(v==f)
continue;
dfs(v,root);
dp[root][0]+=min(dp[v][1],dp[v][2]);//父亲节点如果已经染色 子节点可以染色 也可以不染色
dp[root][1]+=min(dp[v][0],min(dp[v][1],dp[v][2]));//该节点如果已经染色 子节点父节点都可以选择染或者不染
if(dp[v][1]>dp[v][2])//我有很多子节点我需要确定一个最优的子节点
{
dp[root][2]+=dp[v][2];
MIN=min(MIN,dp[v][1]-dp[v][2]);
}
else
{
flag=true;
dp[root][2]+=dp[v][1];
}
}
if(!flag)//如果由于子节点的子节点染色情况比它好 没有一个子节点被染色了 必须要一个最好的来染色
{
dp[root][2]+=MIN;
}
}
int main()
{
memset(dp,0,sizeof(dp));
int i,j,k;
scanf("%d",&n);
for(i=1;i<n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
hh[x].push_back(y);
hh[y].push_back(x);
}
if(n==1||n==2)
{
printf("1\n");
return 0;
}
dfs(1,-1);
printf("%d",min(dp[1][1],dp[1][2]));//因为已经把1看做一个根了
return 0;
}
poj-1463
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<map>
#include<algorithm>
#include<queue>
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
int n;
vector<int>hh[10011];
int dp[10011][2];
bool vis[10011];
int f[1600];
void dfs(int root)
{
if(vis[root])
return ;
vis[root]=true;
dp[root][0]=0;
dp[root][1]=1;
bool flag=false;
for(int i=0;i<n;i++)
{
if(f[i]==root)
{
dfs(i);
dp[root][1]+=min(dp[i][0],dp[i][1]);
dp[root][0]+=dp[i][1];
}
}
}
int main()
{
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
memset(f,-1,sizeof(f));
memset(vis,false,sizeof(vis));
int i,j,k;
for(i=0;i<n;i++)
{
int xh,y;
int num;
scanf("%d:(%d)",&xh,&num);
for(j=0;j<num;j++)
{
scanf("%d",&y);
f[y]=xh;
}
}
if(n==1)
{
printf("0\n");
continue;
}
int root;
for(i=0;i<n;i++)
{
if(f[i]==-1)
root=i;
}
dfs(root);
printf("%d\n",min(dp[root][0],dp[root][1]));
}
return 0;
}