UVA 10859 Placing Lampposts(树DP)

题意:给一个n个顶点m条边的无向无环图,在尽量少的结点上放灯,使得所有边都被照亮。每盏灯将照亮以它为一个端点的所有边。在灯的总数最小的前提下,被两盏灯同时照亮的变数应该尽量大。

思路:无向无环图就是“森林”,常用树形dp,本题要优化的目标有两个,放置的灯数a应尽量少,被两盏灯同时照亮的边数b应尽量大,为了统一,我们把b替换成”恰好被一盏灯照亮的边数c尽量小“。然后设x=Ma+c为最终的优化目标,M是一个很大的正整数。当x取最小值的时候,x/M就是a的最小值,x%M就是c最小值。

当有两个量v1,v2需要优化时,要求首先满足v1最小,在这个前提下v2最小的问题,可以考虑优化x=M*v1+v2,其中M是比"比v2的最大理论值和v2的最小理论值之差"还要大的数。

定义dp(i,j),其中i表示节点i,j表示节点i的父节点是否放置了街灯,0代表没放,1代表放了,则dp(i,j)代表在上述下x的最小值。

实际上,对于每个节点而言,只有两种决策:在i处放或者不放街灯。

决策一:节点i处不放街灯,那么i是根或者父亲节点放了街灯。所以dp(i,j)=sum{ dp(v,0) | v取遍i的所有儿子节点 },如果i不是根节点,那么结果+1,因为i和父亲连接的这条边只被一盏灯照亮。

决策二:节点i处放街灯,dp(i,j)=sum{ dp(v,1)| v取遍i的所有儿子节点  } + M,如果i不是根节点而且j=0,那么结果+1。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#define eps 1e-6
#define LL long long
using namespace std;  

const int maxn = 1000 + 5;
const int M = 2000;
vector<int> G[maxn];
int d[maxn][2];
int m, n;

int dp(int i, int fa, int j) {
	if(d[i][j] != -1) return d[i][j];
	int& ans = d[i][j];
	ans = M;
	for(int k = 0; k < G[i].size(); k++) {
		if(G[i][k] != fa) ans += dp(G[i][k], i, 1);
	}
	if(fa > -1 && !j) ans++;
	int sumv = 0;
	if(fa == -1 || j == 1) {
		if(fa != -1) sumv = 1;
		for(int k = 0; k < G[i].size(); k++) {
			if(G[i][k] != fa) sumv += dp(G[i][k], i, 0);
		}
		ans = min(ans, sumv);
	}
	return ans;
}

int main() {
	//freopen("input.txt", "r", stdin);
	int t; cin >> t;
	while(t--) {
		cin >> n >> m;
		memset(d, -1, sizeof(d));
		for(int i = 0; i < n; i++) G[i].clear();
		for(int i = 0; i < m; i++) {
			int u, v;
			cin >> u >> v;
			G[u].push_back(v);
			G[v].push_back(u);
		}
		int ans = 0;
		for(int i = 0; i < n; i++)
			if(d[i][1] == -1) ans += dp(i, -1, 0);
		int a = ans/M, c = ans%M, b = m-c;
		printf("%d %d %d\n", a, b, c);
	}
	return 0;
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 12:04:47

UVA 10859 Placing Lampposts(树DP)的相关文章

UVA 10859 - Placing Lampposts 树形DP、取双优值

                          Placing Lampposts As a part of the mission ‘Beauti?cation of Dhaka City’, the government has decided to replace all theold lampposts with new expensive ones. Since the new ones are quite expensive and the budget is notup to

UVA 10859 Placing Lampposts 树形dp(水

题目链接:点击打开链接 题意: 白书P70 思路: 简单题,每个点分放或不放. import java.io.PrintWriter; import java.util.ArrayList; import java.util.Scanner; public class Main { int min(int a,int b){return a>b?b:a;} int max(int a,int b){return a>b?a:b;} static int N = 1005; static int

UVA 10859 Placing Lampposts 树形DP

dfs+记忆化搜索,白书上给了一种很神的存答案的方式,要同时保存两个值,可以将一个值乘以一个大整数加上另外一个. 具体状态转移见注释 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <queue>

uva 10859 Placing Lampposts,树形dp

// uva 10859 Placing Lampposts // 树形dp // // 题目的意思是一个无向无环图中,有一些顶点和一些边 // 要在顶点上放置灯笼(灯笼可以照亮与它相邻接的点), // 使得所有的边都能被灯笼照亮,其中可能有一些边被两个灯笼 // 照亮,则要求使得所有边都被灯笼照亮所需灯笼的最小值, // 并且,此时边同时被两个灯笼照亮的数目应尽可能的多 // // 思路是 // d[i][0]表示在节点i不放置灯笼所需的灯笼的最小值 // d[i][1]表示在节点i放置灯笼所

UVA 10859 Placing Lampposts(树形DP)

题意: 给定一张有向无环图,每个节点视作一个路口,每条边视作路,要求挑选一些节点放置路灯,使每条路都能被路灯照到,且使用的路灯数最少,如若存在使用相同路灯数的情况,则使得能被两盏路灯照到的路的数量尽量多. 解题: 可以将此问题提炼一下,就是使用最少的路灯照亮所有的路,使得被两盏路灯照亮的路尽量多,也就是使被一盏路灯照亮的路尽量少.那么问题可以转换为,使用最少x盏路灯,使得最少为y条路被一盏路灯照亮.那么问题就抽象为,W=k*x+y(其中k>y)使得W尽量小.因为,k>y,也就保障了x为首要条件

UVA - 10859 Placing Lampposts

As a part of the mission 'Beautification of Dhaka City', the government has decided to replace all the old lampposts with new expensive ones. Since the new ones are quite expensive and the budget is not up to the requirement, the government has decid

UVa Placing Lampposts 树型DP

大致思路和大白书上的相同,不过感觉书上的决策部分讲解的并不是非常清楚,因此我在这里讲解一下我的决策思路. 首先,d(i,j)表示根节点为i的子树,当它的父节点为j(j=0或1)时的x的最小值(x的含义书上有讲解),要将该子树根节点和父节点相连的边的情况计算在内.接下来遍历森林中的每一棵树,对于每一棵树的根节点进行特别的处理,然后就对该树进行深度优先搜索dfs(i). 对于d[i][0]的情况,因为当前子树根节点i的父节点为0,所以该子树根节点的状态必为1,则d[i][0]=sum{d[k][1]

LightOJ1230 Placing Lampposts(DP)

题目大概说给一个森林求其最小点覆盖数,同时在最小点覆盖条件下输出最多有多少条边被覆盖两次. dp[0/1][u]表示以u为根的子树内的边都被覆盖且u不属于/属于覆盖集所需的最少点数 另外,用cnt[0/1][u]表示满足dp[0/1][u]状态下子树内被覆盖两次最多的边数 对于dp[0][u]只能从其孩子结点v的dp[1][v]转移,而dp[1][u]从dp[0][v]或者dp[1][v]转移,转移时如果同时转移更新cnt的值.. 思路要清晰..转移细节要注意..另外注意是森林. 1 #incl

LightOj 1230 Placing Lampposts(树形DP)

题意:给定一个森林.每个节点上安装一个灯可以覆盖与该节点相连的所有边.选择最少的节点数num覆盖所有的边.在num最小的前提下,合理放置num个灯使得被两个灯覆盖的边最多? 思路:F[i][0]代表没放灯,F[i][1]代表放了灯,G[i]类似. 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6