Uva1218 完美服务(树形dp,树上覆盖问题)

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/88896989

题意

有一个无根树,要选m个点,使得另外的n-m个点都能与这m个点相连,且只能连一个,求最小的m。

题解

经典的覆盖问题,覆盖距离为1,有贪心的解法,也有dp的解法。
这里讲讲dp的解法。

  1. dp[i][0]:选i为服务器。
  2. dp[i][1]:i不是服务器,但i的父亲是。
  3. dp[i][2]:i和i的父亲都不是服务器。但儿子中有一个是服务器。

转移方程:
以下v都代表u的儿子。

  1. dp[u][0] += min{dp[v][0], dp[v][1])如果u是服务器,那么v可以满足1,2。
  2. dp[u][1] += dp[v][2]如果u不是服务器,但父亲是,那么儿子就都不是服务器。这里我有点疑惑,为什么儿子不能也是服务器,仔细读题发现,一个客户端只能连接一个服务器!但其实去掉这个条件,也是没问题的,因为最优的状态一定是中间放服务器,而不是两端放服务器,所以如果dp[v][2]得不出最优解,那么即使v放服务器也是没有效果的。
  3. dp[u][2] = min(dp[u][1]-dp[v][2]+dp[v][0]) 如果u不是服务器且父亲也不是,那么儿子里面肯定要有一个是服务器,这里可以用前面得到的状态dp[u][1]代表儿子全都不是服务器的情况,那么根据容斥原理 全都不是服务器-v不是服务器+v是服务器即得到了答案。

理解的还不够深刻,有错希望有大佬指出!

代码

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;

const int maxn = 10000+5;

int n;
vector<int> G[maxn];
void add(int u, int v) {
	G[u].push_back(v);
}
int dp[maxn][3];

void dfs(int u, int fa) {
	dp[u][0] = 1;
	dp[u][1] = 0;
	for(int i = 0; i < G[u].size(); ++i) {
		int v = G[u][i];
		if(v == fa) continue;
		dfs(v,u);
		dp[u][0] += min(dp[v][0],dp[v][1]);
		dp[u][1] += dp[v][2];
	}

	dp[u][2] = 1000000;
	for(int i = 0; i < G[u].size(); ++i) {
		int v = G[u][i];
		if(v == fa) continue;
		dp[u][2] = min(dp[u][2], dp[u][1]-dp[v][2]+dp[v][0]);
	}
}
int main() {
	while(scanf("%d", &n) && n != -1) {
		for(int i = 0; i <= n; ++i) G[i].clear();
		int u,v;
		for(int i = 0; i < n-1; ++i) {
			scanf("%d%d", &u ,&v);
			add(u,v);
			add(v,u);
		}
		dfs(1,-1);
		scanf("%d", &n);
		cout << min(dp[1][0],dp[1][2]) << endl;		
		if(n == -1) break;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/88896989