zoukankan      html  css  js  c++  java
  • 题解——将军令

    题解——将军令

    *这道题我看到有人打了树形 DP *
    我当时想,每种情况都要讨论,20+的DP方程,那位神仙是给某主播打赏了10万后气急败坏了吗?
    有的时候,可以贪心的别莽着打DP啊


    有道简化版:P2279 [HNOI2003]消防局的设立(树形DP or 贪心)

    题目搬运

    Luogu传送门:P3942 将军令

    又想起了四月。

    如果不是省选,大家大概不会这么轻易地分道扬镳吧? 只见一个又一个昔日的队友离开了机房。

    凭君莫话封侯事,一将功成万骨枯。

    梦里,小 F 成了一个给将军送密信的信使。

    现在,有两封关乎国家生死的密信需要送到前线大将军帐下,路途凶险,时间紧迫。小 F 不因为自己的祸福而避趋之,勇敢地承担了这个任务。

    不过,小 F 实在是太粗心了,他一不小心把两封密信中的一封给弄掉了。

    小 F 偷偷打开了剩下的那封密信。他 发现一副十分详细的地图,以及几句批文——原来 这是战场周围的情报地图。他仔细看后发现,在这张地图上标记了 n 个从 1 到 n 标号的 驿站,n − 1 条长度为 1 里的小道,每条小道双向连接两个不同的驿站,并且驿站之间可以 通过小道两两可达。

    小 F 仔细辨认着上面的批注,突然明白了丢失的信的内容了。原来,每个驿站都可以驻 扎一个小队,每个小队可以控制距离不超过 k 里的驿站。如果有驿站没被控制,就容易产 生危险——因此这种情况应该完全避免。而那封丢失的密信里,就装着朝廷数学重臣留下的 精妙的排布方案,也就是用了最少的小队来控制所有驿站。

    小 F 知道,如果能计算出最优方案的话,也许他就能够将功赎过,免于死罪。他找到了 你,你能帮帮他吗? 当然,小 F 在等待你的支援的过程中,也许已经从图上观察出了一些可能会比较有用的 性质,他会通过一种特殊的方式告诉你。

    解题思路

    一些分析

    1.结构是一棵树,就意味着不会出现环,这里满足无后效性。

    2.DP是不大可能的,我们不可能浪费时间写出巨量的DP转移方程。

    3.答案具有一定程度上的单调(图大致不变,不断增多点数,答案增加)

    4.每一个点都必须处理(这可以是一句废话)

    二次分析

    1.如果我们把节点按照一定的顺序处理,从而使我们安排守卫的收益(覆盖的点更多),那么一定会有一种更优的情况。

    2。如果对一棵树自顶向下处理,在优先考虑了顶部后,我们可能在根节点浪费更多的守卫。(因为每个守卫的控制距离一定,这样等效于降低了守卫的价值)。

    解法

    我们将所有根节点按照深度排序,优先处理深度较大的节点,如果这个点没有被守卫,则在它向上距离最远的地方布置守卫,同时更新标记。

    扫描点,处理深度的时候用BFS是O(n)的,ssw02懒得写,就写了O(nlogn)的dfs+优先队列。

    然后对每个安排守卫的节点进行染色dfs(不然会被菊花图卡掉2个点)

    AC code

    #include<bits/stdc++.h>
    using namespace std ; 
    const  int  MAXN = 1e5+5 ;
    inline  int read(){
    	int  s = 0 ; char  g= getchar() ; while( g>'9'||g<'0')g=getchar() ; 
    	while( g>='0'&&g<='9' )s=s*10+g-'0',g=getchar() ; return s ; 
    } 
    int  dep[ MAXN ] , head[ MAXN*2 ] , to[ MAXN*2 ] , nex[ MAXN*2 ] , tot = 1 ; 
    int  N , M , K , fa[ MAXN ] , ans = 0 ; 
    int vis[ MAXN ] ;
    priority_queue< pair<int,int> >q ; 
    void  add( int x , int y ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ; 
    }
    void  dfs( int u , int father ){//这里推荐写BFS , 少个log
    	fa[ u ] = father ;
    	q.push( make_pair(dep[u],u) ) ; 
    	for( int i = head[ u ] ; i ; i = nex[ i ] ){
    		if( to[ i ] == father )continue ; 
    		dep[ to[ i ] ] = dep[ u ]+ 1 ;
    		dfs( to[ i ] , u ) ; 
    	}  
    }
    void  dfs2( int u , int  dis , int kind  ){ 
        vis[ u ] = kind ; 
    	for( int i = head[ u ] ; i ; i = nex[ i ] )
    		if( dis <= K-1 && vis[ to[ i ] ] != kind  ){//染色,不然会被卡 
    			vis[ to[i] ] = kind ; dfs2( to[ i ] , dis+1 , kind ) ;
    		} 
    }
    int main(){
    	N = read() , K = read() ; int m1 , m2 = read(); 
    	for( int i = 2 ; i <= N ; ++i ){
    		m1 = read() , m2 = read() ; add( m2 , m1 ) , add( m1 , m2 ) ;
    	}
    	dfs( 1 , 0 ) ;
    	while( !q.empty() ){
    		int d = q.top().second ; q.pop(); 		
    		if( !vis[d] ){
    			ans++ ;
    			int  target = d ;
    	        for( int  i = 1 ; fa[ target ]&&i<=K ; ++i )target = fa[ target ] ;//找最远的安排守卫的点
    	        vis[ target ] = target ;
    	        dfs2( target , 0 , target ) ; 
    		}
    	}
    	cout<<ans ; 
    }
    

    树上的点覆盖问题大多都和将军令这道题比较相似,如果题意符合,就可以考虑从根节点向上的贪心策略。

    本文如有不足,请各位大佬指出。

  • 相关阅读:
    函数库:静态库和动态库
    预处理
    共用体、大端小端的判断、枚举
    结构体内存对齐及大小的判断
    内存的管理方式
    指针的高级应用
    H5+css3属性随笔
    项目实战——仿360囧图
    利用css3的动画实现图片轮播
    了解HTML5大纲算法
  • 原文地址:https://www.cnblogs.com/ssw02/p/11301208.html
Copyright © 2011-2022 走看看