考虑最小割,即最少要去掉多少收益
先S向所有机器连边,流量为购买费用;所有机器向工作连边,流量为租借费用;工作向T连边,流量为收益
那么对于每一个工作,要么割掉连向T的边,要么购买/租借所有机器,同时由于购买了就一直可以用,所以购买费用直接连向S

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 3005
 4 struct ji{
 5     int nex,to,len;
 6 }edge[3000005];
 7 queue<int>q;
 8 int E,n,m,x,y,z,ans,head[N],d[N],work[N];
 9 void add(int x,int y,int z){
10     edge[E].nex=head[x];
11     edge[E].to=y;
12     edge[E].len=z;
13     head[x]=E++;
14     if (E&1)add(y,x,0);
15 }
16 bool bfs(){
17     while (!q.empty())q.pop();
18     memset(d,-1,sizeof(d));
19     q.push(0);
20     d[0]=0;
21     while (!q.empty()){
22         int k=q.front();
23         q.pop();
24         for(int i=head[k];i!=-1;i=edge[i].nex)
25             if ((edge[i].len)&&(d[edge[i].to]<0)){
26                 d[edge[i].to]=d[k]+1;
27                 q.push(edge[i].to);
28                 if (edge[i].to>n+m)return 1;
29             }
30     }
31     return 0;
32 }
33 int dfs(int k,int s){
34     if (k>n+m)return s;
35     for(int &i=work[k];i!=-1;i=edge[i].nex)
36         if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){
37             int p=dfs(edge[i].to,min(s,edge[i].len));
38             if (p){
39                 edge[i].len-=p;
40                 edge[i^1].len+=p;
41                 return p;
42             }
43         }
44     return 0;
45 }
46 int main(){
47     scanf("%d%d",&n,&m);
48     memset(head,-1,sizeof(head));
49     for(int i=1;i<=n;i++){
50         scanf("%d%d",&x,&y);
51         ans+=x;
52         add(i+m,n+m+1,x);
53         for(int j=1;j<=y;j++){
54             scanf("%d%d",&x,&z);
55             add(x,i+m,z);
56         }
57     }
58     for(int i=1;i<=m;i++){
59         scanf("%d",&x);
60         add(0,i,x);
61     }
62     while (bfs()){
63         memcpy(work,head,sizeof(head));
64         while (x=dfs(0,0x3f3f3f3f))ans-=x;
65     }
66     printf("%d",ans);
67 }
View Code
01-08 19:15