2015 ACM/ICPC 北京區域賽 現場賽 D—Kejin Game【網路流】
阿新 • • 發佈:2018-12-13
題意:
題意:給一顆有向樹,技能獲得的前提是他的前置技能都獲得了,作為一個玩家,你有特權: 1.直接花費一定數量的錢獲得某個技能。 2.花費一定數量的錢將一個技能的某一個前置關係取消,即將前置技能到該技能的邊消除(不需要獲得前置技能)。 如果正常學習技能的話每一個技能都要花費一定量的時間,問獲得指定的技能的最少的花費是多少。
分析:
網路流 1.將所有點拆成i和i'兩個點,在建好的圖中就用i和i+n分別表示。 2.若i->j有邊(i為j的前置技能),則將i'到j建邊,權值為用錢將該邊消除的花費。 3.將源點和i建邊,邊權為正常學習該技能花費的時間。 4.將i與i'建邊,權值為用錢直接獲得該點的花費。【求這一條邊和(通過減掉前置學習該點的花費/學習前置中最小的+基礎花費)的最小值·】 5.將S’與匯點建邊,權值為inf。 這樣最大流跑完了以後,S’與匯點之間正向邊的流量就是源點與匯點的最小割,也是要求的最小花費。 程式碼:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f #define clear(A,X) memset(A,X,sizeof A) #define copy(A,B) memcpy(A,B,sizeof A) using namespace std; const int maxe=1e6+10; const int maxq=1e6+10; const int maxn=1e5+10; struct Edge{ int v; int c; int n; }edge[maxe]; int adj[maxn],cnte; int Q[maxq],head,tail; int d[maxn],cur[maxn],pre[maxn],num[maxn]; int sourse,sink,nv;//sourse:源點,sink:匯點,nv:編號修改的上限 int n,m; void add(int u,int v,int c) { edge[cnte].v=v; edge[cnte].c=c; edge[cnte].n=adj[u]; adj[u]=cnte++; edge[cnte].v=u; edge[cnte].c=0; edge[cnte].n=adj[v]; adj[v]=cnte++; } void rev_bfs() { clear(num,0); clear(d,-1); d[sink]=0; num[0]=1; head=tail=0; Q[tail++]=sink; while(head!=tail) { int u=Q[head++]; for(int i=adj[u];~i;i=edge[i].n) { int v=edge[i].v; if(~d[v]) continue; d[v]=d[u]+1; Q[tail++]=v; num[d[v]]++; } } } int ISAP() { copy(cur,adj); rev_bfs(); int flow=0,u=pre[sourse]=sourse,i; while(d[sink]<nv) { if(u==sink) { int f=inf,neck; for(i=sourse;i!=sink;i=edge[cur[i]].v) { if(f>edge[cur[i]].c) { f=edge[cur[i]].c; neck=i; } } for(i=sourse;i!=sink;i=edge[cur[i]].v) { edge[cur[i]].c-=f; edge[cur[i]^1].c+=f; } flow+=f; u=neck; } for(i=cur[u];~i;i=edge[i].n) if(d[edge[i].v]+1==d[u]&&edge[i].c) break; if(~i) { cur[u]=i; pre[edge[i].v]=u; u=edge[i].v; } else { if(0==(--num[d[u]])) break; int mind=nv; for(i=adj[u];~i;i=edge[i].n) { if(edge[i].c&&mind>d[edge[i].v]) { cur[u]=i; mind=d[edge[i].v]; } } d[u]=mind+1; num[d[u]]++; u=pre[u]; } } return flow; } void init() { clear(adj,-1); cnte=0; } void work() { int S; scanf("%d%d%d",&n,&m,&S); int u,v,c; init(); for(int i=0;i<m;++i) scanf("%d%d%d",&u,&v,&c),add(u+n,v,c); for(int i=1;i<=n;i++) scanf("%d",&c),add(0,i,c); for(int i=1;i<=n;i++) scanf("%d",&c),add(i,i+n,c); add(S+n,2*n+1,inf); sourse=0;sink=2*n+1;nv=sink+1; printf("%d\n",ISAP()); } int main() { int T,cas=1; scanf("%d",&T); while(T--) work(); return 0; }