题意: n个点,每个点有初始的值 ,三种 通道,1、隧道:可以用来躲避,有固定的容量,也可以用来传递。2、普通的道路,可以无限的通过。3、桥:不花费的话能通过一人,修之后可以无限通过。问最少花费最大可以隐藏人数。
可以建立最大流模型来求解,增加一个源点S,和一个汇点T。S向每个有人的点,连一条容量为人数的边,图中普通的u->v的有向边,连一条u->v的流量为无穷的边,桥的流量则为1。对于隧道,每个隧道可以虚拟出一个点,如u->v的隧道,可以虚拟一个点x,连接u->x,x->v的流量无穷的边,和x->T的流量为隧道人数上限的边,求解最大流即可得到最大人数。
现在考虑桥的问题,题目中说明了桥最多只有12座,故可以2^12枚举修复哪些桥,不修复的桥没有花费,连接的边流量为1,要修复的桥则计算花费,边的流量为无穷,这样进行2^12次最大流就可以得到最优解。
for(int i=0;i<(1<<sum);i++)可以枚举出所有的情况。
if(i&(1<<p[i]))表示枚举该桥修复。
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<string>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include<queue>
#include<cmath>
using namespace std;
const int inf=1<<30;
const int M=500,ME=3009;
const int INF=0x3f3fffff;
//******************************最小费用最大流模版(可以单独用来求最大流,输入的时候,cost=0就可以:适用于u->v,只要符合了流量限制,只要通过了就花费,还有一种是:u->v,每单位的流量必须花费对少费用(poj2159模板))
int Head[M],Next[ME],Num[ME],Flow[ME],Cap[ME],Cost[ME],vis[ME],Q[M],InQ[M],Len[M],pre_edge[M];
int top;
void clear()
{
memset(Head,-1,sizeof(Head));
memset(Flow,0,sizeof(Flow));
memset(vis,0,sizeof(vis));
top=0;
}
void addedge(int u,int v,int cap,int cost)
{
Next[top] = Head[u];
Num[top] = v;
Cap[top] = cap;
Cost[top] = cost;
Head[u] = top++;
Next[top] = Head[v];
Num[top] = u;
Cap[top] = 0;
Cost[top] = -cost;
Head[v] = top++;
}
bool SPFA(int s,int t)
{
fill(Len,Len+M,INF);
Len[s]=0;
int head = -1,tail = -1,cur;
Q[++head] = s;
while(head != tail)
{
++tail;
if(tail >= M) tail = 0 ;
cur = Q[tail];
for(int i = Head[cur];i != -1;i = Next[i])
{
if(Cap[i]>Flow[i] && Len[Num[i]] > Len[cur] + Cost[i])
{
Len[Num[i]] = Len[cur] + Cost[i];
pre_edge[Num[i]] = i;
if(!InQ[Num[i]])
{
InQ[Num[i]]=true;
++head;
if(head >= M) head = 0;
Q[head] = Num[i];
}
}
}
InQ[cur]=false;
}
return Len[t] != INF;
}
int solve(int s,int t)
{
int ans= 0;
while(SPFA(s,t))
{
int cur = t,minflow = INF;
while(cur != s)
{
if(minflow > Cap[pre_edge[cur]]-Flow[pre_edge[cur]])
minflow = Cap[pre_edge[cur]]-Flow[pre_edge[cur]];
cur = Num[pre_edge[cur] ^ 1];
}
cur = t ;
ans+=minflow;
while(cur != s)
{
Flow[pre_edge[cur]] += minflow;
Flow[pre_edge[cur] ^ 1] -= minflow;
vis[pre_edge[cur]]=1;
cur = Num[pre_edge[cur] ^ 1];
}
}
return ans;//最大流
int cost;//最小费用
for(int i=0;i<top;i++) //适用于u->v,只要符合了流量限制,只要通过了就花费,不管通过多少流量
{
if(Flow[i]>0)
cost+=Cost[i];
}
}
//****************************************************************
int n,m;
int val[M];
int u[1509],v[1509],w[1509],p[1509];
int uu[15],vv[15],ww[15];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
clear();
int start=0;
int end=n+25;
int b=n+1;
int k=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
addedge(start,i,val[i],0);
}
for(int j=1;j<=m;j++)
{
scanf("%d%d%d%d",&u[j],&v[j],&w[j],&p[j]);
if(p[j]==0)
{
addedge(u[j],v[j],inf,0);
}
else if(p[j]<0)
{
addedge(u[j],b,inf,0);
addedge(b,v[j],inf,0);
addedge(b++,end,w[j],0);
}
else
{
addedge(u[j],v[j],inf,0);
uu[k]=u[j];
vv[k]=v[j];
ww[k]=w[j];
k++;
p[k]=k;
}
}
int an=solve(start,end);
int mm=1<<k;
int co=inf;
for(int i=0;i<mm;i++)
{
clear();
b=n+1;
for(int j=1;j<=n;j++)
{
addedge(start,j,val[j],0);
}
for(int j=1;j<=m;j++)
{
if(p[j]==0)
{
addedge(u[j],v[j],inf,0);
}
else if(p[j]<0)
{
addedge(u[j],b,inf,0);
addedge(b,v[j],inf,0);
addedge(b++,end,w[j],0);
}
else
{
addedge(u[j],v[j],1,0);
if((1<<(p[j]-1))&i)
addedge(u[j],v[j],inf,0);
}
}
int tmp=solve(start,end);
if(tmp==an)
{
int r=0;
for(int t=0;t<k;t++)
{
if(i&(1<<t))
r+=ww[t];
}
if(r<co)
co=r;
}
}
if(an==0)
puts("Poor Heaven Empire");
else
printf("%d %d\n",an,co);
}
return 0;
}