*【POJ - 3659】Cell Phone Network (树形dp,最小支配集)

题干:

Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.

Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A≤ N; 1 ≤ B ≤ NA ≠ B) there is a sequence of adjacent pastures such that is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.

Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.

Input

* Line 1: A single integer: N
* Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B

Output

* Line 1: A single integer indicating the minimum number of towers to install

Sample Input

5
1 3
5 2
4 3
3 5

Sample Output

2

解题报告:

由于这是在树上求最值的问题,显然可以用树形动态规划,只是状态的设计比较复杂。为了保证动态规划的正确性,对于每个点设计了三种状态,这三种状态的意义如下:

①dp[i][2]:表示点i属于支配集,并且以点i为根的子树都被覆盖了的情况下支配集中所包含的的最少点的个数。

②dp[i][1]:i不属于支配集,且以i为根的子树都被覆盖,且i被其中不少于1个子节点覆盖的情况下支配集中所包含最少点的个数。

③dp[i][0]:i不属于支配集,且以i为根的子树都被覆盖,且i没被子节点覆盖的情况下支配集中所包含最少点的个数。

对于第一种状态,dp[i][0]等于每个儿子节点的3种状态(其儿子是否被覆盖没有关系)的最小值之和加1,即只要每个以i的儿子为根的子树都被覆盖,再加上当前点i,所需要的最少点的个数,方程如下:

dp[i][2]=1+Σmin(dp[u][2],dp[u][1],dp[u][0])(p[u]=i).

对于第二种状态,如果点i没有子节点,那么dp[i][1]=INF;否则,需要保证它的每个以i的儿子为根的子树都被覆盖,那么要取每个儿子节点的前两种状态的最小值之和,因为此时i点不属于支配集,不能支配其子节点,所以子节点必须已经被支配,与子节点的第三种状态无关。如果当前所选的状态中,每个儿子都没有被选择进入支配集,即在每个儿子的前两种状态中,第一种状态都不是所需点最少的,那么为了满足第二种状态的定义,需要重新选择点i的一个儿子的状态为第一种状态,这时取花费最少的一个点,即取min(dp[u][2]-dp[u][1])的儿子节点u,强制取其第一种状态,其他儿子节点都取第二种状态,转移方程为:

if(i没有子节点)dp[i][1]=INF

else dp[i][1]=Σmin(dp[u][2],dp[u][1])+inc

其中对于inc有:

if(上面式子中的Σmin(dp[u][2],dp[u][1])中包含某个dp[u][2])inc=0;

else inc=min(dp[u][2]-dp[u][1])。

对于第三种状态,i不属于支配集,且以i为根的子树都被覆盖,又i没被子节点覆盖,那么说明点i和点i的儿子节点都不属于支配集,则点i的第三种状态只与其儿子的第二种状态有关,方程为

dp[i][0]=Σdp[u][1]

AC代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
const int INF = 0x3f3f3f3f;
int head[MAX];
struct Edge {
	int to,ne;
} e[MAX];
int tot;
void add(int u,int v) {
	e[++tot].to =v;
	e[tot].ne = head[u];
	head[u] = tot;
}
int Min(int a,int b,int c) {
	if(a < b) return min(a,c);
	else return min(b,c);
}
int Min(int a,int b) {
	return min(a,b);
}
int dp[MAX][3];//0不选并且没有被覆盖掉,1不选但是被覆盖到了,2选 ,(同时要保证子节点全部被覆盖到了) 
void dfs(int cur,int rt) {
	dp[cur][2] = 1;
	int flag = 1,tmp=INF;
	for(int i = head[cur]; i!=-1; i=e[i].ne) {
		int v = e[i].to;
		if(v == rt) continue;
		dfs(v,cur);
		dp[cur][2] += Min(dp[v][0],dp[v][1],dp[v][2]);
		dp[cur][0] += Min(dp[v][2],dp[v][1]); //不能由下一层没被覆盖到转移!!
		if(dp[v][2]<=dp[v][1]) {
			dp[cur][1]+=dp[v][2];
			flag=0;
		} 
		else {
			dp[cur][1]+=dp[v][1];
			tmp=min(tmp,dp[v][2]-dp[v][1]);
		}
	}
	if(flag)    //还没有选儿子,加上这个差值,补回一个儿子
		dp[cur][1]+=tmp;
}
int main() {
	int n;
	cin>>n;
	memset(head,-1,sizeof head);
	for(int a,b,i = 1; i<=n-1; i++) {
		scanf("%d%d",&a,&b);
		add(a,b);
		add(b,a);
	}
	dfs(1,-1);
	printf("%d\n",Min(dp[1][1],dp[1][2]));
	return 0 ;
}

但是不是很理解为什么  dp[cur][0] +=dp[v][1];  就WA?

 

他这样写:

按照这个定义:

①dp[i][0]:表示点i属于支配集,并且以点i为根的子树都被覆盖了的情况下支配集中所包含的的最少点的个数。

②dp[i][1]:i不属于支配集,且以i为根的子树都被覆盖,且i被其中不少于1个子节点覆盖的情况下支配集中所包含最少点的个数。

③dp[i][2]:i不属于支配集,且以i为根的子树都被覆盖,且i没被子节点覆盖的情况下支配集中所包含最少点的个数。

void DP(int u,int p) {
	dp[u][2]=0;
	dp[u][0]=1;
	bool s=false;
	int sum=0,inc=INF;
	int k;
	for(k=head[u]; k!=-1; k=edge[k].next) {
		int to=edge[k].to;
		if(to==p)continue;
		DP(to,u);
		dp[u][0]+=min(dp[to][0],min(dp[u][1],dp[u][2]));
		if(dp[to][0]<=dp[to][1]) {
			sum+=dp[to][0];
			s=true;
		} else {
			sum+=dp[to][1];
			inc=min(inc,dp[to][0]-dp[to][1]);
		}
		if(dp[to][1]!=INF&&dp[u][2]!=INF)dp[u][2]+=dp[to][1];
		else dp[u][2]=INF;
	}
	if(inc==INF&&!s)dp[u][1]=INF;
	else {
		dp[u][1]=sum;
		if(!s)dp[u][1]+=inc;
	}
}

也是对的,,,这样就可以   dp[cur][2] 只跟 dp[v][1];有关????why??? 

 


另一个题解:

*【POJ - 3659】Cell Phone Network (树形dp,最小支配集)_第1张图片

#include
#include
#define clr(x)memset(x,0,sizeof(x))
int min(int a,int b)
{ return a=dp[k][0])
            {
                ff=0;
                dp[r][1]+=dp[k][0];
            }
            else 
            {
                dp[r][1]+=dp[k][1];
                if(dp[k][0]

 

你可能感兴趣的:(动态规划(dp),优秀模板,POJ)