UVa 1218 - Perfect Service

/*---UVa 1218 - Perfect Service
---首先对状态进行划分:
---dp[u][0]:u是服务器,则u的子节点可以是也可以不是服务器
---dp[u][1]:u不是服务器,但u的父节点是服务器,则u的所有儿子节点都不是服务器
---dp[u][2]:u和u的父亲都不是服务器,则u的儿子恰好有一个是服务器
---状态转移方程:
---dp[u][0]=sum{min(dp[v][0],dp[v][1])}+1
---dp[u][1]=sum(dp[v][2]);
---对于状态dp[u][2],计算略微复杂,这个状态说明u的儿子节点中恰好有一个是服务器,于是需要枚举每一个儿子节点是服务器
---剩下儿子不是服务器的情况,考虑到d(u,1)=sum(dp[v][2]),所以每次枚举时,不必再累加子节点不是服务器的情况,因为这样
---会使得计算一个节点复杂度达到O(k^2),k是u的子节点个数,可以:dp[u][2]=min(dp[u][1]-dp[v][2]+dp[v][0]),枚举v即可
---在实现时,首先递归的构造有根树。然后可以采用记忆化搜索。
---初始化问题,若u是叶子节点,dp[u][0]=1,dp[u][1]=0,dp[u][2]=INF,服务器个数不会超过10000,所以为了保准累加结果不溢出
---可以将INF设置为10000.
*/
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
#define INF 10000+10;
const int maxn = 10000 + 10;

int d[maxn][3];
int parent[maxn];
vector<int>vec[maxn];

//构造有根树
void dfs(int u, int fa){
	parent[u] = fa;
	for (int i = 0; i < vec[u].size(); i++){
		int v = vec[u][i];
		if (v != fa) dfs(v, u);
	}
}

int dp(int u, int k){
	int&ans = d[u][k];
	if (ans >= 0)return ans;
	int n = vec[u].size();
	if (k == 0)ans = 1;
	else if (k == 1)ans = 0;
	else ans = INF;
	if (n == 1 && parent[u] == vec[u][0]){ //叶节点
		return ans;
	}
	for (int i = 0; i < n; i++){
		int v = vec[u][i];
		if (v == parent[u])continue;  //v是u的父节点,则跳过
		if (k == 0)ans += min(dp(v, 0), dp(v, 1));
		else if (k == 1) ans += dp(v, 2);
		else ans = min(ans, dp(u, 1) - dp(v, 2) + dp(v, 0));
	}
	return ans;
}
int main(){
	int n, i,u,v;
	while (scanf("%d", &n)){
		for (i = 0; i <= n; i++)vec[i].clear();

		for (i = 1; i < n; i++){
			scanf("%d%d", &u, &v);
			u--, v--;
			vec[u].push_back(v);
			vec[v].push_back(u);
		}
		scanf("%d", &v);
		dfs(0, -1); 
		vec[0].push_back(-1);
		memset(d, -1, sizeof(d));
		int ans = min(dp(0, 0), dp(0, 2));
		printf("%d\n", ans);
		if (v == -1)break;
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/td15980891505/p/5798854.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值