消防局的设立&&将军令//贪心
消防局有DP做法,而且要是按省选难度看的话那个题应该是DP
昨天跟冯神讨论了一下,如果点上设权,那么就只能DP做了
下面DP部分的详细讲解转载自luogu CaptainSlow 的题解(这里是他的博客)
DP[i][state] 表示 i 当前子树根节点 state就是一个一个的状态 state = 0, 1, 2 : DP[i][0] 表示 选 i 为消防局 DP[i][1] 表示 {至少} 选了 i 的一个儿子为消防局 DP[i][2] 表示 {至少} 选了 i 的一个孙子为消防局 ==================================以上三种状态是 i {一定被消防局覆盖} 的情况 state = 3, 4 : DP[i][3] 表示 i 的 {所有} 儿子节点一定被消防局覆盖 DP[i][4] 表示 i 的 {所有} 孙子节点一定被消防局覆盖 ==================================以上两种状态是 i {不一定被消防局覆盖} 的情况
(以下j, k均表示i的儿子节点)
DP[i][0] = 1 + Σ min ( DP[j][0...4] ); 由于当 i 为消防站之后儿子和孙子是否为消防局就无关紧要,因此状态在0~4中寻找一个MIN,然后还需要+1(因为自己是消防局) DP[i][1] = min ( DP[k][0] + Σ ( j != k ) min ( DP[j][0...3] ) ); 由于儿子为消防站时只能覆盖到兄弟(这里不含所选儿子的儿子),要使选了一个儿子之后子树中包括自己的所有节点都被覆盖,所以除了这个选的儿子,其他儿子的儿子一定要全部被覆盖,状态0~3满足。 DP[i][2] = min ( DP[k][1] + Σ ( j != k ) min ( DP[j][0...2] ) ); 要使选了一个孙子之后合法,由于孙子最多只能覆盖到当前节点,所以其他儿子一定全部覆盖, 状态0~2满足 DP[i][3] = Σ min(DP[j][0...2]); 要使儿子及孙子全部被覆盖,即儿子本身要被覆盖,状态0~2满足 DP[i][4] = Σ min(DP[j][0...3]); 孙子全部被覆盖 ,状态0~3满足
加速:令 DP[i][j] 表示 min ( DP[i][0..k] ) (2<=k<=4) 因此可以得到: DP[i][0] = 1 + Σ DP[j][4]; DP[i][1] = DP[i][4] + min ( DP[k][0] - DP[k][3] ); DP[i][2] = DP[i][3] + min ( DP[k][1] - DP[k][2] ); DP[i][3] = Σ DP[j][2]; DP[i][4] = Σ DP[j][3];
正确DP很神,很难设计状态,不太可做
我们可以口胡一个贪心,照样可以A这道题
我们随便pick一个点为根,然后算出每个点的深度
由于深度最深的点必须被覆盖,所以想到覆盖掉它的最好位置就是它的爷爷
为什么不是它的兄弟?因为你是最深的点,设在你的爷爷上必能覆盖你的兄弟,
但是设在兄弟上覆盖不到爷爷的爷爷,从正确性上讲,贪心得到证明
每次用优先队列找深度最大的点
AC代码:
#include<bits/stdc++.h> using namespace std ; const int MAXN = 1010; struct Edge{ int nxt,to; }edge[MAXN<<1]; int head[MAXN],ectr; void addedge(int from,int to){ edge[++ectr].to = to; edge[ectr].nxt = head[from]; head[from] = ectr; } int n,dep[MAXN],father[MAXN],book[MAXN]; struct Node{ int ID,dep; }; priority_queue<Node> q; bool operator < (Node a,Node b){ return a.dep < b.dep; } void dfs1(int x,int fa){ dep[x] = dep[fa] + 1; father[x] = fa; for(int i=head[x];i;i=edge[i].nxt){ int to = edge[i].to; if(to == fa) continue; dfs1(to,x); } } int find (int x,int now){ if(x == 1 || now == 0) return x; else return find(father[x],now-1); } void tatoo(int x,int fa,int now){ book[x] = true; if(now == 0) return; for(int i=head[x]; i; i=edge[i].nxt){ int to = edge[i].to; if(to == fa) continue; tatoo(to, x, now - 1); } } int main(){ cin>>n; for(int i=2;i<=n;++i){ int aa; cin>>aa; addedge(aa,i); addedge(i,aa); } for(int i=head[1]; i; i=edge[i].nxt){ int to = edge[i].to; dfs1(to,1); } for(int i=1;i<=n;i++){ Node xxx;xxx.ID = i; xxx.dep = dep[i]; q.push(xxx); } long long ans = 0; while(!q.empty()){ Node xxx = q.top(); q.pop(); if(book[xxx.ID]) continue; ++ans; int se = find(xxx.ID,2); // cout<<"ans = "<<ans<<" se = "<<se<<endl; tatoo(se,-1,2); } cout<<ans<<endl; return 0; }
同时有一道同做法的题,而且只能贪心,叫将军令
这里贴出AC代码,跟上面差了几个变量名的事
#include<bits/stdc++.h> using namespace std ; const int MAXN = 100010; struct Edge{ int nxt,to; }edge[MAXN<<1]; int head[MAXN],ectr; void addedge(int from,int to){ edge[++ectr].to = to; edge[ectr].nxt = head[from]; head[from] = ectr; } int n,k,t,dep[MAXN],father[MAXN],book[MAXN]; struct Node{ int ID,dep; }; priority_queue<Node> q; bool operator < (Node a,Node b){ return a.dep<b.dep; } void dfs1(int x,int fa){ dep[x] = dep[fa] + 1; father[x] = fa; for(int i=head[x];i;i=edge[i].nxt){ int to = edge[i].to; if(to == fa) continue; dfs1(to,x); } } int find (int x,int now){ if(x == 1 || now == 0) return x; else return find(father[x],now-1); } void tatoo(int x,int fa,int now){ book[x] = true; if(now == 0) return; for(int i=head[x];i;i=edge[i].nxt){ int to = edge[i].to; if(to == fa) continue; tatoo(to, x, now - 1); } } int main(){ cin>>n>>k>>t; for(int i=1;i<n;++i){ int a,b; cin>>a>>b; addedge(a,b); addedge(b,a); } for(int i=head[1];i;i=edge[i].nxt){ int to = edge[i].to; dfs1(to,1); } for(int i=1;i<=n;i++){ Node xxx;xxx.ID = i; xxx.dep = dep[i]; q.push(xxx); } long long ans = 0; while(!q.empty()){ Node xxx = q.top(); q.pop(); if(book[xxx.ID]) continue; ++ans; int se = find(xxx.ID,k); tatoo(se,-1,k); } // cout<<find(3,1); cout<<ans<<endl; return 0; }