题目描述
五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。
已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。
一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。
任务:
请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。
解析
树上dp
状态定义:𝑓[𝑥][0/1/2]分别对应以下三种情况:
0.x节点被自己覆盖,即选择x点来覆盖x点
1.x节点被儿子y覆盖,即选择y点来覆盖x点
2.x节点被父亲fa覆盖,即选择fa点来覆盖x点
转移为:
𝑓[𝑥][0]=∑ min(𝑓[𝑦][0],𝑓[𝑦][1],𝑓[𝑦][2]) + 𝑣𝑎𝑙[𝑥]
𝑓[𝑥][2]=∑ min(𝑓[𝑦][0],𝑓[𝑦][1])
𝑓[𝑥][1]=∑ min(𝑓[𝑦][0],𝑓[𝑦][1])
第三种如果选择的全部都是𝑓[𝑦][1],要再加上min(𝑓[𝑦][0]−𝑓[𝑦][1])
然后就完事了
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1800;
int n,m;
/*
op=0 被自己覆盖
op=1 被儿子覆盖
op=2 被父亲覆盖
*/
struct node{int to,nxt;
}p[N];
int fi[N],cnt=-1,ru[N];
int v[N];
void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
}
int a,b,c;
int dp[N][5];
int find(int x,int op){if(dp[x][op]) return dp[x][op];int ans=0;
// printf("op=%d\n",op);if(op==0){ans=v[x];for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;ans+=min(find(to,0),min(find(to,1),find(to,2)));}
// printf("%d %d %d\n",x,op,ans);return dp[x][op]=ans;}else if(op==2){for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;ans+=min(find(to,0),find(to,1));}
// printf("%d %d %d\n",x,op,ans);return dp[x][op]=ans;}else if(op==1){int flag=1,mn=2e9;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(find(to,0)<find(to,1)) flag=0;if(flag){mn=min(mn,find(to,0)-find(to,1));}ans+=min(find(to,0),find(to,1));}if(flag) ans+=mn;
// printf("%d %d %d\n",x,op,ans);return dp[x][op]=ans;}
}
int main(){memset(fi,-1,sizeof(fi));scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d%d",&a,&b,&c);v[a]=b;for(int j=1;j<=c;j++){scanf("%d",&b);addline(a,b);ru[b]++;}}for(int i=1;i<=n;i++){if(ru[i]==0){printf("%d",min(find(i,0),find(i,1)));break;}}return 0;
}
/*
6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0
*/