1. 程式人生 > >洛谷2899 手機網路(樹形DP)

洛谷2899 手機網路(樹形DP)

傳送門

【題目分析】

N個點,N-1條邊,那麼這就是一棵樹,再看看這種父親與兒子之間的相互影響的關係,那麼就鎖定樹形DP做法。

當我們確定了一個點為根的時候,所有點的父子關係都確定了。

考慮一個點,有三種選擇:1.自己建 2.自己不建,由兒子將自己覆蓋 3.自己不建,由父親覆蓋,所以開dp[MAXN][3]記錄3個狀態的最優解。

考慮轉移。如果當前點選擇自己建,那麼他的兒子就有兩種選擇:1和3;

如果當前點選擇自己不建由兒子覆蓋,那麼他的兒子中就至少有一個要選擇建,所以遞迴處理兒子建和不建的最優解,再選擇哪些兒子要建。

如果當前點被父親覆蓋,那麼當前點就不用考慮,直接統計所有兒子最優解即可。

【程式碼~】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;
const int MAXM=2e4+10;
const int INF=0x3f3f3f3f;

int n,cnt;
int head[MAXN];
int nxt[MAXM],to[MAXM];
int dp[MAXN][3],sum[MAXN];

int Read(){
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

void add(int x,int y){
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
	cnt++;
}

int DP(int u,int fa,int stau){
	if(dp[u][stau]!=-1)
	  return dp[u][stau];
	if(stau==0){
		dp[u][stau]=1;
		for(int i=head[u];i!=-1;i=nxt[i]){
			int v=to[i];
			if(v==fa)
			  continue;
			dp[u][stau]+=min(DP(v,u,0),DP(v,u,2));
		}
	}
	if(stau==1){
		dp[u][stau]=INF;
		int total=0;
		for(int i=head[u];i!=-1;i=nxt[i]){
			int v=to[i];
			if(v==fa)
			  continue;
			sum[v]=min(DP(v,u,0),DP(v,u,1));
			total+=sum[v];
		}
		for(int i=head[u];i!=-1;i=nxt[i]){
			int v=to[i];
			if(v==fa)
			  continue;
			dp[u][stau]=min(dp[u][stau],DP(v,u,0)+total-sum[v]);
		}
	}
	if(stau==2){
		dp[u][stau]=0;
		for(int i=head[u];i!=-1;i=nxt[i]){
			int v=to[i];
			if(v==fa)
			  continue;
			dp[u][stau]+=min(DP(v,u,0),DP(v,u,1));
		}
	}
	return dp[u][stau];
}

int main(){
	memset(head,-1,sizeof(head));
	memset(dp,-1,sizeof(dp));
	n=Read();
	for(int i=1;i<n;++i){
		int x=Read(),y=Read();
		add(x,y),add(y,x);
	}
	cout<<min(DP(1,-1,0),DP(1,-1,1));
	return 0;
}