题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3917
思路:就是一个最大权闭包的模型,解法为求最小割,构图方法以公司为点,如果公司之间有关系,则连一条容量为无穷的有向边,对每个公司,如果该公司收益为正,则从vs到该公司连一条容量为收益的有向边,如果为负,向vt连一条容量为该公司收益绝对值的边,为0时选和不选不影响结果,可以舍去。其消费为所有收益和-所选割中正收益-所有割中负收益(减去负的即为加上绝对值),然后求出最小割,最后相减即可。(胡伯涛的论文《最小割模型在信息学竞赛中的应用》里面有这个模型)
View Code
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define MAXN 5555 7 #define MAXM 2222222 8 #define inf 1<<30 9 10 struct Edge{ 11 int v,cap,next; 12 }edge[MAXM]; 13 14 int head[MAXN]; 15 int pre[MAXN]; 16 int cur[MAXN]; 17 int level[MAXN]; 18 int gap[MAXN]; 19 int NV,NE,vs,vt,n,m,f,sum; 20 21 vector<int>st[MAXN],ed[MAXN]; 22 int tax[MAXN]; 23 24 void Insert(int u,int v,int cap,int cc=0){ 25 edge[NE].v=v;edge[NE].cap=cap; 26 edge[NE].next=head[u];head[u]=NE++; 27 28 edge[NE].v=u;edge[NE].cap=cc; 29 edge[NE].next=head[v];head[v]=NE++; 30 } 31 32 33 int SAP(int vs,int vt){ 34 memset(pre,-1,sizeof(pre)); 35 memset(level,0,sizeof(level)); 36 memset(gap,0,sizeof(gap)); 37 for(int i=0;i<=NV;i++)cur[i]=head[i]; 38 int u=pre[vs]=vs,maxflow=0,aug=-1; 39 gap[0]=NV; 40 while(level[vs]<NV){ 41 loop: 42 for(int &i=cur[u];i!=-1;i=edge[i].next){ 43 int v=edge[i].v; 44 if(edge[i].cap&&level[u]==level[v]+1){ 45 aug==-1?aug=edge[i].cap:aug=min(aug,edge[i].cap); 46 pre[v]=u; 47 u=v; 48 if(v==vt){ 49 maxflow+=aug; 50 for(u=pre[u];v!=vs;v=u,u=pre[u]){ 51 edge[cur[u]].cap-=aug; 52 edge[cur[u]^1].cap+=aug; 53 } 54 aug=-1; 55 } 56 goto loop; 57 } 58 } 59 int minlevel=NV; 60 for(int i=head[u];i!=-1;i=edge[i].next){ 61 int v=edge[i].v; 62 if(edge[i].cap&&minlevel>level[v]){ 63 cur[u]=i; 64 minlevel=level[v]; 65 } 66 } 67 if(--gap[level[u]]==0)break; 68 level[u]=minlevel+1; 69 gap[level[u]]++; 70 u=pre[u]; 71 } 72 return maxflow; 73 } 74 75 void Build(){ 76 for(int i=1;i<=m;i++){ 77 if(tax[i]>0)Insert(vs,i,tax[i]),sum+=tax[i]; 78 else if(tax[i]<0)Insert(i,vt,-tax[i]); 79 } 80 for(int i=1;i<=n;i++){ 81 for(int j=0;j<st[i].size();j++){ 82 for(int k=0;k<ed[i].size();k++){ 83 Insert(ed[i][k],st[i][j],inf);//这里建边要注意了!(是以顶点i结尾的点所在的那个公司连以顶点i开始的所在的公司) 84 } 85 } 86 } 87 } 88 89 90 int main(){ 91 while(~scanf("%d%d",&n,&m)&&(n+m)){ 92 int u,v,company,cost; 93 for(int i=1;i<=n;i++)st[i].clear(),ed[i].clear(); 94 for(int i=1;i<=m;i++){ 95 scanf("%d",&tax[i]); 96 } 97 scanf("%d",&f); 98 for(int i=1;i<=f;i++){ 99 scanf("%d%d%d%d",&u,&v,&company,&cost); 100 st[u].push_back(company); 101 ed[v].push_back(company); 102 tax[company]-=cost;//要减去补偿给每个公司的money 103 } 104 memset(head,-1,sizeof(head)); 105 vs=0,vt=m+1,NE=0,NV=m+2,sum=0; 106 Build(); 107 printf("%d\n",sum-SAP(vs,vt)); 108 } 109 return 0; 110 } 111 112