题意复杂,而且感觉很不严谨,大概的意思就是给几个公司,告诉每个公司你税收,这个公司要修得路(如果选择了某个公司,则必须要全修,如果有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;
}