传送门
解析:
最小割入门题。
思路:
我们考虑直接做出全部的任务,然后考虑我们可能获得的最小亏损,其中放弃一个任务也是一种亏损,租用或购买一个机器也是一种亏损。
我们这样建图,从源点向每个任务连容量为其价值的边,保证这个任务会带来的购买或租用机器造成的亏损不超过它的价值。
我们从每个任务向它的工序需要的机器连容量为租用费用的边,再从机器向汇点连容量为购买费用的边。
这样求出来的最小割就是我们的最小亏损,因为割掉图中流量流满的部分边 之后我们就无法完成上述任务了,这个直接求最大流就好了。
Update:
我解释一下这样建图最终跑出来的网络流边的最终流量的意义。
首先是任务与源点之间的边,最终残量(即流量与容量之差)的意义就是这个任务能够带来的最大收益,如果流满说明这个任务应当放弃,因为我们租用或购买了所有它需要的机器的费用不能被它的价值填补。
对于机器而言,它和汇点的边如果流满,说明它需要被购买,否则,它只需要在前面的流满的边中被租用就行了。
其实主要思想就是,我们用源点到任务的流量限制了我们在每个任务上的新增花费(有些机器或许为了其他的任务已经被购买)不会超过这个任务的价值。
用机器到汇点的流量限制了每个机器如果在被租用的时候花费过多,不如直接购买。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=2403,M=2000006;
cs int S=0,T=N-1;
int last[N],nxt[M<<1],to[M<<1],ecnt=1;
int cap[M<<1];
inline void addedge(int u,int v,int val){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0;
}
int lev[N],cur[N];
inline bool BFS(){
queue<int> q;
memset(lev,-1,sizeof lev);
q.push(S),lev[S]=1;
cur[S]=last[S];
while(!q.empty()){
int u=q.front();
q.pop();
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]==-1){
lev[v]=lev[u]+1;
if(v==T)return true;
cur[v]=last[v];
q.push(v);
}
}
}
return false;
}
inline int Dinic(cs int &u,cs int &flow){
if(u==T)return flow;
int ans=0;
for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]>lev[u]){
int delta=Dinic(v,min(flow-ans,cap[e]));
if(delta){
cap[e]-=delta;
cap[e^1]+=delta;
ans+=delta;
if(ans==flow)return flow;
}
}
}
lev[u]=-1;
return ans;
}
inline int maxflow(){
int ans=0;
while(BFS())ans+=Dinic(S,0x3f3f3f3f);
return ans;
}
int n,m,ans;
signed main(){
n=getint();
m=getint();
for(int re i=1;i<=n;++i){
int A=getint();
addedge(S,i,A);
ans+=A;
A=getint();
while(A--){
int u=getint(),cost=getint();
addedge(i,u+1200,cost);
}
}
for(int re i=1;i<=m;++i){
int val=getint();
addedge(i+1200,T,val);
}
cout<<ans-maxflow();
return 0;
}