洛谷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;
}

猜你喜欢

转载自blog.csdn.net/g21glf/article/details/83780316