zoukankan      html  css  js  c++  java
  • [Luogu] P2899 [USACO08JAN]Cell Phone Network G

    (Link)

    Description

    John想让他的所有牛用上手机以便相互交流,他需要建立几座信号塔在(N)块草地中。已知与信号塔相邻的草地能收到信号。给你(N-1)个草地((A,B))的相邻关系,问:最少需要建多少个信号塔能实现所有草地都有信号。

    Solution

    妙妙的树形(DP)

    显然(x)只能被自己的儿子/父亲染色后波及,或者自己就已经被染色。我们设(dp[x][0/1/2])分别表示(x)是在(x)自己/自己的儿子/自己的父亲染色后被波及(被其他点波及的,自己肯定就不用染色了),且子树全被波及所需要的最少染色次数。设(x)的儿子为(y),再分别进行转移:

    (1.dp[x][0]=sum{min(dp[y][0],dp[y][1],dp[y][2])})(y)可以自己染色,被(x)波及,和被它的儿子波及)

    (2.dp[x][2]=sum{min(dp[y][0],dp[y][1])})(y)可以自己染色和被它的儿子波及,但不会被(x)波及)

    (dp[x][1])的转移比较复杂。显然它也只会由(dp[y][0])(dp[y][1])转移而来,但具体要怎么转移呢?

    我们贪心地想,肯定是要取(min(dp[y][0],dp[y][1]))。但比较特殊的是,(dp[x][1])也不能全由(dp[y][1])转移来,因为它至少要有一个自己被染色的儿子来波及它。

    所以我们先把(dp[x][1])加上(sum{dp[y][0]}),再把(dp[y][1]-dp[y][0])的值存到一个队列(que)里。处理完(x)的所有子节点后,再来处理(dp[x][1])。显然,若(que[i]<0),那么我们就把(dp[y][0])替换成(dp[y][1])(即(dp[x][1]+=que[i]))。所以直接从小到大排序,如果(que[i]>0)(break)掉。这样也能保证替换的值是最优的。还要注意两点:首先如果(x)没有儿子,那么(dp[x][1])要设成(INF)。其次设(que[])(t)个元素,我们只能枚举到(t-1)个元素,原因之前已经说过了。

    最后要注意每次(dfs)要新开一个队列,不能用全局变量!!(可能是因为回溯之类的奇奇怪怪的原因吧)

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int n, tot, hd[100005], to[200005], nxt[200005], dp[100005][3];
    
    int read()
    {
    	int x = 0, fl = 1; char ch = getchar();
    	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
    	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    	return x * fl;
    }
    
    void add(int x, int y)
    {
    	tot ++ ;
    	to[tot] = y;
    	nxt[tot] = hd[x];
    	hd[x] = tot;
    	return;
    }
    
    void dfs(int x, int fa)
    {
    	dp[x][0] = 1; int t = 0, que[100005];
    	for (int i = hd[x]; i; i = nxt[i])
    	{
    		int y = to[i];
    		if (y == fa) continue;
    		dfs(y, x);
    		dp[x][0] += min(dp[y][1], dp[y][2]);
    		dp[x][1] += dp[y][0];
    		dp[x][2] += min(dp[y][0], dp[y][1]);
    		que[ ++ t] = dp[y][1] - dp[y][0];
    	}
    	sort(que + 1, que + t + 1);
    	if (!t) dp[x][1] = 1e9;
    	else
    	{
    		for (int i = 1; i < t; i ++ )
    		{
    			if (que[i] < 0) dp[x][1] += que[i];
    			else break;
    		}
    	}
    	for (int i = 1; i <= t; i ++ )
    		que[i] = 0;
    	return;
    }
    
    int main()
    {
    	n = read();
    	for (int i = 1; i <= n - 1; i ++ )
    	{
    		int x = read(), y = read();
    		add(x, y); add(y, x);
    	}
    	dfs(1, 0);
    	printf("%d
    ", min(dp[1][0], dp[1][1]));
    	return 0;
    }
    
  • 相关阅读:
    linux下利用valgrind工具进行内存泄露检测和性能分析
    Linux下内存泄漏工具
    linux下将不同线程绑定到不同core和cpu上——pthread_setaffinity_np
    Linux下getopt()函数
    SparkStreaming+Kafka整合
    Hive+Sqoop+Mysql整合
    crontab
    Hive与Hbase整合
    关系数据库数据与hadoop数据进行转换的工具
    Flume+Kafka+Storm+Hbase+HDSF+Poi整合
  • 原文地址:https://www.cnblogs.com/andysj/p/13922228.html
Copyright © 2011-2022 走看看