题意:求树上最小支配集
最小支配集:点集,即每个点可以“支配”到相邻点,求最少点数可以使所有点被支配。
图上的最小支配集是NP的,但是树上的可以DP做,是O(n)的。
暴力做就好了,
f[i]表示此 点被选时的子树全支配的最小代价
g[i]表示其父亲节 点被选时的子树全支配的最小代价
h[i]表示其某子节 点被选时的子树全支配的最小代价
然后暴力转移。
(v是子节点)
f[x]=∑(min(f[v],min(g[v],h[v])))+1;
g[x]=∑(min(f[v],h[v]));
h[x]: Ⅰ. 某个f[v]<=h[v]:h[x]=g[x]
Ⅱ. 取个影响最小的f[v],即此v有f[v]-h[v]最小。其它v取min(f[v],h[v])此时即h[v]
呃,这个是正规做法,先附个代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10100
#define inf 0x3f3f3f3f
using namespace std;
struct KSD
{
int v,next;
}e[N<<1];
int head[N],cnt;
void add(int u,int v)
{
cnt++;
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int n,f[N],g[N],h[N];
// 亮、父亮、子亮
void Tree_DP(int x,int p)
{
int i,v,temp=20000;
f[x]=1;
bool flag=1,flag2=0;
for(i=head[x];i;i=e[i].next)
{
v=e[i].v;
if(v==p)continue;
Tree_DP(v,x);
f[x]+=min(f[v],min(g[v],h[v]));
g[x]+=min(f[v],h[v]);
temp=min(temp,f[v]-h[v]);
if(f[v]<=h[v])flag2=1;
flag=0;
}
if(flag)h[x]=inf;
else {
h[x]=g[x];
if(!flag2)h[x]+=temp;
}
return ;
}
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
int a,b,c;
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
Tree_DP(1,0);
printf("%d\n",min(f[1],h[1]));
return 0;
}
再附个很久以前写的贪心:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 10100
typedef struct KSD{int v,next;}ksd;ksd e[2*N];
int head[N],n,ans;
int attack[N],killed[N];
void add(int note,int u,int v){e[note].v=v;e[note].next=head[u];head[u]=note;}
void init()
{
ans=0;
memset(head,-1,sizeof(head));
memset(attack,0,sizeof(attack));
memset(killed,0,sizeof(killed));
}
void dfs(int x,int p)
{
int i,j,k,t,v;
for(t=head[x];t!=-1;t=e[t].next)
{
if(e[t].v!=p)
dfs(e[t].v,x);
}
if(killed[x]==0)
{
ans++;
attack[p]=killed[x]=killed[p]=1;
for(t=head[p];t!=-1;t=e[t].next)
{
killed[e[t].v]=1;
}
}
}
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
int a,b,c;
while(scanf("%d",&n)!=EOF)
{
init();
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(i*2-1,a,b);
add(i*2,b,a);
}
dfs(1,0);
printf("%d\n",ans);
}
return 0;