uva 10859 Placing Lampposts,树形dp

// uva 10859 Placing Lampposts
// 树形dp
//
// 题目的意思是一个无向无环图中,有一些顶点和一些边
// 要在顶点上放置灯笼(灯笼可以照亮与它相邻接的点),
// 使得所有的边都能被灯笼照亮,其中可能有一些边被两个灯笼
// 照亮,则要求使得所有边都被灯笼照亮所需灯笼的最小值,
// 并且,此时边同时被两个灯笼照亮的数目应尽可能的多
//
// 思路是
// d[i][0]表示在节点i不放置灯笼所需的灯笼的最小值
// d[i][1]表示在节点i放置灯笼所需的灯笼的最小值
//
//	先说明一下一下j表示i的子节点
//
//
// 则状态转移方程为
// d[i][0] += d[j][1];(这条路必须要被照亮)
// d[i][1] += max(d[j][1],d[j][0])
//
// 再定义一个f
// f[i][0]表示节点i放置灯笼边同时被两个灯笼照亮的总数最大值
// f[i][1]表示节点i放置灯笼边同时被两个灯笼照亮的总数最大值
//
// 则状态转移可能有点复杂
// 如下所示:
// 首先f数组肯定要根据d数组来递推
//
// f[i][0] += f[j][1] (没得选,i到j必须被照亮)
//
// f[i][1] 应该首先根据d[j][0] 和 d[j][1] 来
// 如果后面的两者不相等,则取较小的第二维记作mk(保证总的灯笼最小)
// 此时f[i][1] += f[j][mk] + mk ? 1 : 0; (如果mk==1则i到j的边被两个灯笼
// 照亮,即加一)
//
// 如果d[j][0] == d[j][1];
//
// 则此时f[i][1] += max(f[j][0],f[j][1] + 1)(在i放灯笼的情况下,j放不放灯笼
// 所需的灯笼的总数都是一样的,那么就看j放灯笼的 f[j][1]+1 大
// 还是不放灯笼的f[j][0]大)
//
// 至此,这道题目算是思路很清楚了。另外就是要注意图可能是不连通的
//
// 下面说说这题给我带来的感受。以及自身的一丝变化。
//
// 这一题是我真真正正自己想出来,并努力自己做出来的题目。看到mw给我看的一组
// ACM前辈的问答:为什么当初我们是一个水平,但是后来你进了final,我却没有
// 回答是:我在有了一定的基础之后就不看解题报告了。这句话给我一直很大的冲击
// 如果是刚入门的时候,你看看题解,没人会说你什么,因为都是菜鸟,看看题解,
// 入门会快一些,也会多一些经验,在入了门以后,再看题解,也没人会说什么,自
// 己会清楚自己的状态,如果你认为看题解对你有帮助,那就看呗,如果不是为自己
// 毫无意义的虚荣心的话。
//
// 虽然我以前经常在无助的时候看题解,也并不明白为什么是这样,所以我反复的提
// 交,反复的看,反复的提交,在自己不明白的地方就加上自己理解的代码再提交。
// 虽然当时真的感觉自己吃透这一题了,但是在过了不久之后再看到这一题的时候还
// 是感觉像是新的题目一样。如果你大多数都是看题解的话,建议你把以前的题目在
// 做一遍,如果没有我这样的感受,我只能在这里膜拜一发各位巨巨。但对于我来说
// 就是这样。
//
// 这道题目其实训练指南上有详细的解题指导,我也看了,但是完全看不懂在讲什么
// 在自己卡了两天,wrong answer 了好久之后,这段时间我真的是忍不住差点跟着
// 书上的思路敲一遍算了的想法出现,但最终我还是忍住了,一遍一遍的调试,最后
// 的结果还是挺让人高兴的。那种accept的感受不是简单的过了的感受,而是自己的
// 成果最终是有回报的。在此,我要感谢mw,是他点醒了我,会求一个值,就会求
// 第二个值。猛然醒悟。
// 虽然这题仔细想来并不是很难,但这是
// 我感受到了自己的喜悦,不多说,继续努力,付出总会有回报。fighting
//

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define ceil(a,b) (((a)+(b)-1)/(b))
#define endl '\n'
#define gcd __gcd
#define highBit(x) (1ULL<<(63-__builtin_clzll(x)))
#define popCount __builtin_popcountll
typedef long long ll;
using namespace std;
const int MOD = 1000000007;
const long double PI = acos(-1.L);

template<class T> inline T lcm(const T& a, const T& b) { return a/gcd(a, b)*b; }
template<class T> inline T lowBit(const T& x) { return x&-x; }
template<class T> inline T maximize(T& a, const T& b) { return a=a<b?b:a; }
template<class T> inline T minimize(T& a, const T& b) { return a=a<b?a:b; }

const int maxn = 1008;
int head[maxn];
struct node{
	int to;
	int next;
}edges[maxn*2];
int num;

int n,m;
int f[maxn][2];
int d[maxn][2];
bool vis[maxn];
void add_edges(int u,int v){
	edges[num].to = v;
	edges[num].next = head[u];
	head[u] = num++;
}

void init(){
	memset(f,0,sizeof(f));
	memset(head,-1,sizeof(head));
	memset(vis,0,sizeof(vis));
	num=0;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add_edges(u,v);
		add_edges(v,u);
	}
}

void dfs(int u,int fa){
	d[u][0] = 0;
	d[u][1] = 1;
	vis[u] = true;
	for (int i=head[u];i!=-1;i=edges[i].next){
		int v = edges[i].to;
		if (!vis[v]){
			dfs(v,u);
			d[u][0] += d[v][1];
			int mk = -1;
			if (d[v][0] < d[v][1]){
				mk = 0;
			}else {
				mk = 1;
			}
			d[u][1] += d[v][mk];
			int temp = 0;
			temp = f[v][mk] + (mk?1:0);
			if (d[v][1]==d[v][0])
				temp =  max(temp,max(f[v][1]+1,f[v][0]));
			f[u][1] += temp;
			//d[u][1] += min(d[v][0],d[v][1]);
			f[u][0] += f[v][1];
		}
	}
}

void print(){
	for (int i=0;i<n;i++){
		printf("%d %d\n",f[i][0],f[i][1]);
	}
}

void solve(){
	int sum = 0;
	int mx = 0;
	for (int i=0;i<n;i++)
		if (!vis[i]){
			dfs(i,-1);
			int mk=0;
			if (d[i][0]<d[i][1])
				mk = 0;
			else
				mk = 1;
			if (d[i][0] == d[i][1])
				mk = f[i][0] > f[i][1] ? 0 : 1 ;

			sum += d[i][mk];
			mx += f[i][mk];
		}
	//print();
	//cout << mk << endl;
	//mn = min(d[0][0],d[0][1]);
	printf("%d %d %d\n",sum,mx,m-mx);
}

int main() {
	int t;
	//freopen("G:\\Code\\1.txt","r",stdin);
	scanf("%d",&t);
	while(t--){
		init();
		solve();
	}
	return 0;
}

时间: 2024-08-03 16:12:17

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)

题意:给一个n个顶点m条边的无向无环图,在尽量少的结点上放灯,使得所有边都被照亮.每盏灯将照亮以它为一个端点的所有边.在灯的总数最小的前提下,被两盏灯同时照亮的变数应该尽量大. 思路:无向无环图就是"森林",常用树形dp,本题要优化的目标有两个,放置的灯数a应尽量少,被两盏灯同时照亮的边数b应尽量大,为了统一,我们把b替换成"恰好被一盏灯照亮的边数c尽量小".然后设x=Ma+c为最终的优化目标,M是一个很大的正整数.当x取最小值的时候,x/M就是a的最小值,x%M就

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

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

uva 12186 Another Crisis 树形dp

// uva 12186 Another Crisis 树形dp // // 对于一个节点u,有k个子节点,则至少有c = (k * T - 1) / 100 + 1才能 // 发信,即c / k >= T / 100,则 c 的值为 k * T /100,上取整变成上式 // 将所有的子节点d从小到大排序,取前c个就是d[u]的值 // 紫书上的一题,之前看了好久好久,觉得挺好的,然而一直没做,今天就来 // 体验体验,挺好的一题,注意一下,如果一个节点是叶节点,直接return 1就好 //

UVA - 1218 Perfect Service(树形dp)

题目链接:id=36043">UVA - 1218 Perfect Service 题意 有n台电脑.互相以无根树的方式连接,现要将当中一部分电脑作为server,且要求每台电脑必须连接且仅仅能连接一台server(不包含作为server的电脑).求最少须要多少台电脑作为server. 思路 典型的树形dp问题,那么我们来建立模型. d(u,0):u是server,孩子是不是server均可 d(u,1):u不是server,u的父亲是server,u的孩子不能是server d(u,2)