• 51nod 1378:夹克老爷的愤怒 很好玩的一道树状dp


    基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
     收藏
     取消关注
    夹克老爷逢三抽一之后,由于采用了新师爷的策略,乡民们叫苦不堪,开始组织起来暴力抗租。
    夹克老爷很愤怒,他决定派家丁常驻村中进行镇压。
    诺德县 有N个村庄,编号0 至 N-1,这些村庄之间用N - 1条道路连接起来。
    家丁都是经过系统训练的暴力机器,每名家丁可以被派驻在一个村庄,并镇压当前村庄以及距离该村庄不超过K段道路的村庄。
    夹克老爷一贯奉行最小成本最大利润的原则,请问要实现对全部村庄的武力控制,夹克老爷需要派出最少多少名家丁?

    Input
    第1行:2个数N, K中间用空格分隔(1<= N <= 100000, 0 <= K <= N)。
    之后N-1行:每行2个数S, E中间用空格分隔,表示编号为S的村庄同编号为E的村庄之间有道路相连。(0 <= S, E < N)。
    Output
    输出一个数说明要实现对全部村庄的武力控制,夹克老爷需要派出最少多少名家丁?
    Input示例
    4 1
    0 1
    0 2
    0 3
    Output示例
    1

    官方题解:树形DP,贪心思想,从叶子节点向上,能不放就不放,到了k长就放一个。
    后序遍历,记录不同子树上传的状态,子树状态记录为该子树可以向上管理的(缺少的用负数)
    可能A子树放置的家丁可以把B子树的村庄全部覆盖,这样就可以节约家丁数了。
    min_length = min(dp[child])
    max_length = max(dp[child])
    if(min_length <= -K) {
        ++result;
        dp[cur_idx] = K;   //子树需要暴力支援深度达到K了,必须在当前位置放置一个家丁,并向上提供K的暴力输出
    } else if(max_length + min_length > 0) {
        dp[cur_idx] = max_child - 1;      //有一个子树放置的家丁能够把全部其他需要镇压的村庄都覆盖
    } else {
        dp[cur_idx] = min_child - 1;      //继续向上级要求暴力支持
    }

    最后如果root的状态小于0要额外多放一个。


    这个题目真的挺好玩的,把这n个村庄n-1个连接想象成是一个树,然后记录每一个节点缺少的级别,当到达了k个级别的时候就要加上一个家丁,此时这个节点缺少的级别是正的k,因为它要开始照顾到它的兄弟子树了。

    手动扩栈扩栈也是头一次。。。

    代码:

    #pragma comment(linker, "/STACK:1024000000,1024000000")  
    #include <iostream>  
    #include <algorithm>  
    #include <cmath>  
    #include <vector>  
    #include <string>  
    #include <cstring>  
    #pragma warning(disable:4996)  
    using namespace std;
    
    #define inf 0x3f3f3f3f
    
    int res;
    int n, k;
    int s, e; 
    int used[100050];
    int dp[100050];
    vector<int>node[100050];
    
    void dfs(int x)
    {
    	used[x] = 1;
    	int minn = inf;
    	int maxn = -inf;
    	int size = node[x].size();
    
    	int i;
    	for (i = 0; i < size; i++)
    	{
    		int temp = node[x][i];
    		if (used[temp] == 0)
    		{
    			dfs(temp);
    			minn = min(minn, dp[temp]);
    			maxn = max(maxn, dp[temp]);
    		}
    	}
    	if (minn == inf)
    	{
    		dp[x] = -1;
    	}
    	else if(minn<=-k)
    	{
    		res++;
    		dp[x] = k;
    	}
    	else if (maxn + minn > 0)
    	{
    		dp[x] = maxn - 1;
    	}
    	else
    	{
    		dp[x] = minn - 1;
    	}
    	used[x] = 0;
    }
    
    int main()
    {
    	//freopen("i.txt", "r", stdin);
    	//freopen("o.txt", "w", stdout);
    	
    	int i;
    	scanf("%d%d", &n, &k);
    	
    	for (i = 1; i <= n - 1; i++)
    	{
    		scanf("%d%d", &s, &e);
    		s++;
    		e++;
    		node[s].push_back(e);
    		node[e].push_back(s);
    	}
    	if (k == 0)
    	{
    		printf("%d", n);
    		return 0;
    	}
    	dfs(1);
    
    	if (dp[1] < 0)
    	{
    		res++;
    	}
    	printf("%d", res);
    	//system("pause");
    	return 0;
    }


  • 相关阅读:
    浏览器返回错误汇总分析
    黄金投资品种众多 个人如何投资黄金
    .NET设计模式系列文章 (转自TerryLee's Tech Space)
    .NET设计模式(7):创建型模式专题总结(Creational Pattern)
    一个很经典的下拉式菜单(附效果)
    .NET设计模式(16):模版方法(Template Method)
    搜索引擎优化基础(转并整理添加)
    .NET设计模式(18):迭代器模式(Iterator Pattern)
    【ASP.NET】网页中嵌入视频的三种方法
    .NET设计模式(15):结构型模式专题总结
  • 原文地址:https://www.cnblogs.com/lightspeedsmallson/p/5173973.html
Copyright © 2020-2023  润新知