题解——将军令

题解——将军令

*这道题我看到有人打了树形 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 ; 
}

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

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

posted @ 2019-08-05 09:43  蓝银杏-SSW  阅读(334)  评论(0编辑  收藏  举报
//结束