[51nod1299]监狱逃离 树形DP || 20w个点的网络流最小割ORZ
監獄有N條道路連接N + 1個交點,編號0至N,整個監獄被這些道路連在一起(任何2點之間都有道路),人們通過道路在交點之間走來走去。其中的一些交點只有一條路連接,這些點是監獄的出口。在各個交點中有M個點住著犯人(M <= N + 1),剩下的點可以安排警衛,有警衛把守的地方犯人無法通過。給出整個監獄的道路情況,以及犯人所在的位置,問至少需要安排多少個警衛,才能保證沒有1個犯人能夠逃到出口,如果總有犯人能夠逃出去,輸出-1。
如上圖所示,點1,6 住著犯人,0,4,5,7,8是出口,至少需要安排4個警衛。
Input
第1行:2個數N, M中間用空格分隔,N表示道路的數量,M表示犯人的數量(1<= N <= 100000, 0 <= M <= N + 1)。
之后N行:每行2個數S, E中間用空格分隔,表示點編號為S的點同編號為E的點之間有道路相連。(0 <= S, E <= N)。
之后的M行,每行1個數Pi,表示編號為Pi的點上有犯人。
Output
輸出1個數對應最少需要多少警衛才能不讓犯人逃出監獄。
Input示例
8 2
0 1
1 2
2 3
3 4
3 5
2 6
6 8
6 7
1
6
Output示例
4
ORZ ,學長在網絡流專題里放這么一道毒瘤題,20w個點,他說可以用網絡流遛一遛。。。。 我自己寫的網絡流交了十多發才卡過去。。。。
ヽ(ー_ー)ノ
正解應該是樹形DP,那時候并不了解樹形DP。。 看題解都理解了好久。。。。。
這道題跟UVA1218的狀態轉移方程有一些相似。有興趣的可以去看一哈。 也挺有意思的
**
解題思路
**
他給出的是一棵樹,求的是最小花費問題,圖上的最小花費一般是最短路,網絡流,或者DP問題了。 這里由于20w個點,網絡流應該首先被pass掉(實在不行也可以試一發,暴力出奇跡)。
這題看不出跟最短路有啥關系,我們應該往樹形DP的方向去想。
我們來考慮一下,放完警衛后,這個樹形圖上每個點的狀態會是怎么樣的。
狀態一. 這個點的子樹上的逃犯逃不到這個點,這個點也無法通向的它子樹的葉子節點
狀態二. 這個點的子樹上的逃犯逃不到這個點,這個點有通向其葉子節點的路徑
狀態三. 這個點的子樹上的逃犯能到達這個節點,這個節點無法通向其子樹的葉子節點。
除此之外,如果這個圖擁有其他狀態的點,那么絕對不合法。
這里在我們更新子節點的時候不用考慮這個節點父親節點的狀況,因為父親節點的狀態就是由子節點唯一確定的。
狀態很少,所以我們可以很輕易的枚舉所有狀態。 由此可以寫出dp數組的定義
怎么轉移呢。 分類討論一下,判斷出每種狀態的優先級不停if else即可。。。。。。。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> #include<queue> #include<cmath> #include<limits> #include<map> #include<set> #include<stack> #include<string> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define fuck(x) cout<<"["<<x<<"]"<<endl #define mem(a,b) memset(a,b,sizeof a); class edges { public:int u,v,next; }; edges edge[100005*2]; int tot=0; int head[100005]; int ans=0; void init() {mem(head,-1);tot=0;ans=0; } void add(int u,int v) {edge[tot].u=u; edge[tot].v=v;edge[tot].next=head[u];head[u]=tot; tot++; } int people[100005]; int dp[100005]; // 0表示子節點的逃犯無法到達這個節點,該節點可通向葉子結點。 既葉子節點的初始狀態 // 1表示 表示子節點逃犯無法到達這個節點,該節點不可通向葉子結點 // 2表示該節點的子樹的有逃犯可以到達該節點,且該節點不可以通向葉子節點 囚犯所在的節點都會是這個狀態 void dfs(int root,int per) {int s[3]={0,0,0};for(int i=head[root];i!=-1;i=edge[i].next){int v=edge[i].v;if(v==per)continue;dfs(v,root);s[dp[v]]++;}if(people[root]){dp[root]=2;ans+=s[0];}else if(s[2]&&s[0]){ans++;dp[root]=1;}else if(s[0]){dp[root]=0;}else if(s[2]){dp[root]=2;}else if(s[1]){dp[root]=1;} } int du[100005]; int main() {int n,m;scanf("%d %d",&n,&m);init();for(int i=1;i<=n;i++){int u,v;scanf("%d %d",&u,&v);add(u,v);add(v,u);du[u]++;du[v]++;}for(int i=1;i<=m;i++){int root;scanf("%d",&root);people[root]=1;}for(int i=1;i<n;i++){if(du[i]==1){dfs(i,-1);if(dp[i]==2)ans++;cout<<ans<<endl;break;}}return 0; }總結
以上是生活随笔為你收集整理的[51nod1299]监狱逃离 树形DP || 20w个点的网络流最小割ORZ的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云南毒贩越狱出逃 监狱安防漏洞都在哪儿?
- 下一篇: 【排序】一次查找两元素