警卫站岗(树上dp)
生活随笔
收集整理的這篇文章主要介紹了
警卫站岗(树上dp)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目描述
五一來臨,某地下超市為了便于疏通和指揮密集的人員和車輛,以免造成超市內的混亂和擁擠,準備臨時從外單位調用部分保安來維持交通秩序。
已知整個地下超市的所有通道呈一棵樹的形狀;某些通道之間可以互相望見。總經理要求所有通道的每個端點(樹的頂點)都要有人全天候看守,在不同的通道端點安排保安所需的費用不同。
一個保安一旦站在某個通道的其中一個端點,那么他除了能看守住他所站的那個端點,也能看到這個通道的另一個端點,所以一個保安可能同時能看守住多個端點(樹的結點),因此沒有必要在每個通道的端點都安排保安。
任務:
請你幫助超市經理策劃安排,在能看守全部通道端點的前提下,使得花費的經費最少。
解析
樹上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 */總結
以上是生活随笔為你收集整理的警卫站岗(树上dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不止代码:洛谷P1064 金明的预算方案
- 下一篇: 洛谷P4322 最佳团伙(树上dp)