题意复杂,而且感觉很不严谨,大概的意思就是给几个公司,告诉每个公司你税收,这个公司要修得路(如果选择了某个公司,则必须要全修,如果有associated关系,要连锁选择)及花费,求政府的最大收益(所选的公司的收税减其花费)。
解法为求最小割,构图方法以公司为点,如果公司之间有关系,则连一条容量为无穷的有向边,对每个公司,如果该公司收益为正,则从s到该公司连一条容量为收益的有向边,如果为负向t连一条容量为该公司收益绝对值的边,为0时选和不选不影响结果,可以舍去。每一种可行情况都对应图中的一个ST割,其消费为所有收益和-所选割中正收益-所有割中负收益(减去负的即为加上绝对值),又因为公司之间边为正无穷,最小割一定存在,则一定不包含公司间的边,这样若求最大收益,即求图中最小割。
用的SAP的优化算法 跑
312ms,只要是懒的模拟数组临界表了
#include <cstdio> #include <string.h> #include <vector> using namespace std; const int N=5010; const int M=100000;//边是双向存的(注意不是无向)要开正常的2倍大 const int inf=0x3fffffff; int head[N]; struct Edge{ int v,next,w; } edge[M]; int cnt,s=0; vector <int> st[N],to[N];//st[i],to[i]表示以city i位起点和终点的公司 void addedge(int u,int v,int w)//这里存的还是一条有向边 { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt++; } int sap(int t){ int pre[N],cur[N],dis[N],gap[N]; int flow=0,aug=inf,u; bool flag; for(int i=0; i<=t; i++){ cur[i]=head[i]; gap[i]=dis[i]=0; } gap[s]=t+1; u=pre[s]=s; while(dis[s]<=t){ flag=0; for(int &j=cur[u]; ~j; j=edge[j].next){ int v=edge[j].v; if(edge[j].w>0&&dis[u]==dis[v]+1){ flag=1; if(edge[j].w<aug) aug=edge[j].w; pre[v]=u; u=v; if(u==t){ flow+=aug; //printf("%d\n",flow); while(u!=s){ u=pre[u]; edge[cur[u]].w-=aug; edge[cur[u]^1].w+=aug;//异或是找与其配对的边 } aug=inf; } break; } } if(flag) continue; int mindis=t+1; for(int j=head[u]; ~j; j=edge[j].next){ int v=edge[j].v; if(edge[j].w>0&&dis[v]<mindis){ mindis=dis[v]; cur[u]=j; } } if((--gap[dis[u]])==0) break; gap[dis[u]=mindis+1]++; u=pre[u]; } return flow; } int tax[N]; int sum; int n,m,k; void build () { for (int i=1; i<=m ; ++i) { if(tax[i]>0)addedge(0,i,tax[i]),sum+=tax[i]; if(tax[i]<0)addedge(i,m+1,-tax[i]); } for (int i=0 ; i<n ; ++i) for (int j=0 ; j<st[i].size() ; ++j) for (int p=0 ; p<to[i].size() ; ++p) addedge(to[i][p],st[i][j],inf); //printf("%d\n",sum); } void init () { sum=cnt=0; memset(head,-1,sizeof(head)); } int main() { int i,j,u,v,w,company; //freopen ("a.txt","r",stdin); //freopen ("b.txt","w",stdout); while(scanf("%d%d",&n,&m),n||m) { for (i=0 ; i<n ; ++i) st[i].clear(),to[i].clear(); init(); for (i=1 ; i<=m ; ++i) scanf("%d",tax+i); scanf("%d",&k); for (i=0 ; i<k ; ++i) { scanf("%d%d%d%d",&u,&v,&company,&w); u--;v--; st[u].push_back(company); to[v].push_back(company); tax[company]-=w; } build(); int ans=sap(m+1); printf("%d\n",sum-ans); } return 0; }