洛谷 P3931 SAC E#1 - 一道難題 Tree
阿新 • • 發佈:2020-11-29
洛谷 P3931 SAC E#1 - 一道難題 Tree
題目背景
冴月麟和魏瀟承是好朋友。
題目描述
冴月麟為了守護幻想鄉,而製造了幻想鄉的倒影,將真實的幻想鄉封印了。任何人都無法進入真實的幻想鄉了,但是她給前來救她的魏瀟承留了一個線索。
她設定了一棵樹(有根)。樹的每一條邊上具有割掉該邊的代價。
魏瀟承需要計算出割開這棵樹的最小代價,這就是冴月麟和魏瀟承約定的小祕密。
幫幫魏瀟承吧。
注:所謂割開一棵有根樹,就是刪除若干條邊,使得任何任何葉子節點和根節點不連通。
輸入格式
輸入第一行兩個整數n,S表示樹的節點個數和根。
接下來n-1行每行三個整數a、b、c,表示a、b之間有一條代價為c的邊。
輸出格式
輸出包含一行,一個整數,表示所求最小代價。
題解:
題意就很像最小割裸題。但是最小割只要求匯點與源點不聯通,這道題要求所有葉子節點都不連通。很容易啊,建超級匯點就好了。
然後根據最大流最小割定理,直接在上面跑Dinic最大流。
程式碼:(不知道為什麼,這份程式碼會MLE一半的點,如有大佬請指正)
#include<cstdio> #include<cstring> #include<queue> using namespace std; const int maxn=1e5+5; const int INF=1e9; int n,s,t,ans; int tot=1,head[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1]; int lv[maxn],cur[maxn]; bool v[maxn]; queue<int> q; void add(int x,int y,int z) { to[++tot]=y; nxt[tot]=head[x]; val[tot]=z; head[x]=tot; } void dfs_pre(int x,int f) { bool flag=0; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==f) continue; flag=1; dfs_pre(y,x); val[i^1]=0; } if(!flag) { add(x,t,INF); add(t,x,0); } } bool bfs() { memset(lv,0,sizeof(lv)); lv[s]=1; q.push(s); while(!q.empty()) { int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(!val[i]||lv[y]) continue; lv[y]=lv[x]+1; q.push(y); } } return lv[t]; } int dfs(int x,int flow) { if(!flow||x==t) return flow; int re=0; for(int i=cur[x];i;i=nxt[i]) { cur[x]=i; int y=to[i]; if(val[i]>0 && lv[y]==lv[x]+1) { int tmp=dfs(y,min(val[i],flow)); if(!tmp) continue; flow-=tmp; re+=tmp; val[i]-=tmp; val[i^1]+=tmp; } } return re; } int main() { scanf("%d%d",&n,&s); t=n+1; for(int i=1;i<n;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } dfs_pre(s,-1); while(bfs()) { memcpy(cur,head,sizeof(head)); ans+=dfs(s,INF); } printf("%d\n",ans); return 0; }