题意:有一个技能学习表,是一个DAG,要想正常学习到技能x,要将指向x的技能全部先学到,然后会有一个正常花费cx。然后你还有一种方案,通过氪金dx直接获得技能x。你还可以通过一定的代价,切断一条边。问你学得指定的技能N的最小代价。
源点向每个点连接代价为cx的边,每个点拆点,内部连接代价为dx的边,然后N向汇点连接代价为无穷的边,然后每条原图中的边的容量为切断其的代价。
容易发现,每一个割集的方案恰好对应一种学习到N的所需代价的方案。所以直接跑最小割即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; typedef long long ll; #define INF 2147483647000ll #define MAXN 1005 #define MAXM 32005 ll cap[MAXM]; int v[MAXM],en,first[MAXN],nex[MAXM]; int d[MAXN],cur[MAXN]; queue<int>q; int S,T; void Init_Dinic(){memset(first,-1,sizeof(first)); en=0;} void AddEdge(const int &U,const int &V,const ll &W) { v[en]=V; cap[en]=W; nex[en]=first[U]; first[U]=en++; v[en]=U; cap[en]=0; nex[en]=first[V]; first[V]=en++; } bool bfs() { memset(d,-1,sizeof(d)); q.push(S); d[S]=0; while(!q.empty()) { int U=q.front(); q.pop(); for(int i=first[U];i!=-1;i=nex[i]) if(d[v[i]]==-1 && cap[i]) { d[v[i]]=d[U]+1; q.push(v[i]); } } return d[T]!=-1; } ll dfs(int U,ll a) { if(U==T || !a) return a; ll Flow=0,f; for(int &i=cur[U];i!=-1;i=nex[i]) if(d[U]+1==d[v[i]] && (f=dfs(v[i],min(a,cap[i])))) { cap[i]-=f; cap[i^1]+=f; Flow+=f; a-=f; if(!a) break; } if(!Flow) d[U]=-1; return Flow; } ll max_flow() { ll Flow=0,tmp=0; while(bfs()) { memcpy(cur,first,sizeof(first)); while(tmp=dfs(S,INF)) Flow+=tmp; } return Flow; } int n,m,nn; int main(){ // freopen("d.in","r",stdin); int x,y,zu; ll z; scanf("%d",&zu); for(;zu;--zu){ Init_Dinic(); scanf("%d%d%d",&n,&m,&nn); S=n*2+1; T=S+1; for(int i=1;i<=m;++i){ scanf("%d%d%lld",&x,&y,&z); AddEdge(x+n,y,z); } for(int i=1;i<=n;++i){ scanf("%lld",&z); AddEdge(S,i,z); } for(int i=1;i<=n;++i){ scanf("%lld",&z); AddEdge(i,i+n,z); } AddEdge(nn+n,T,INF); printf("%lld\n",max_flow()); } return 0; }