題目鏈接
F 發傳單
題意:
一個人有 個朋友,這個人有很多傳單,他可以將傳單發給所有朋友,對於發給第 個人需要 的費用,他的朋友之間也有相互認識可以將傳單給其他人,也需要一些費用,告訴你具體的朋友之間的認識關係和費用,要求這個人在使用最少的傳單數下用最少的總費用,使得朋友都看過傳單。
思路:
由於要求每一個朋友都看過,那麼可以將每一個朋友進行拆點 那麼這兩點之間的流量一定要大於等於 ,將人做爲源點 按照題意建邊,建立一個匯點 ,向所有的朋友右端點連向 ,由於要求使用最小的傳單數,參考一血的代碼,可以將人向每一個朋友傳單的費用變爲 ,最後 就行了,因爲是最小費用,所以可以使得所用的傳單最小。現在就只要建立超級源點 和超級匯點 就可以跑有上下界的最小費用最大流了。
代碼:
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
const int mx=2e6;
struct MCMF{
int h[N],ne[M],to[M],flow[M],w[M],tot,dis[N],liu[N],pre[N],inq[N],s,t;
int ans;
void init(int a,int b){
s=a;t=b;
for(int i=0;i<M;i++)ne[i]=0;
for(int i=0;i<N;i++)h[i]=0;
tot=1;ans=0;
}
void addedge(int x,int y,int z,int c){
to[++tot]=y;ne[tot]=h[x],h[x]=tot,flow[tot]=z;w[tot]=c;
swap(x,y);
to[++tot]=y;ne[tot]=h[x],h[x]=tot,flow[tot]=0;w[tot]=-c;
}
int bfs(){
for(int i=0;i<=t;i++)dis[i]=inf,liu[i]=inf,inq[i]=pre[i]=0;
queue<int>q;int x;dis[s]=0;q.push(s);
while(!q.empty()){
x=q.front();q.pop();inq[x]=0;
for(int i=h[x];i;i=ne[i]){
int v=to[i];
if(flow[i]>0&&dis[x]+w[i]<dis[v]){
dis[v]=dis[x]+w[i],pre[v]=i;
liu[v]=min(liu[x],flow[i]);
if(!inq[v])inq[v]=1,q.push(v);
}
}
}
if(!pre[t])return 0;
x=t;ans+=dis[t]*liu[t];
while(x!=s){
int kl=pre[x];
flow[kl]-=liu[t];flow[kl^1]+=liu[t];x=to[kl^1];
}
return 1;
}
int mcmf(){
while(bfs()){}
return ans;
}
}F;
int d[N];
void add(int u,int v,int down,int up,int w){
F.addedge(u,v,up-down,w);
d[u]-=down,d[v]+=down;
}
int n,s,t,S,T;
int main()
{
scanf("%d",&n);s=2*n+1;t=2*n+2;S=2*n+3,T=2*n+4;
F.init(S,T);//初始化
add(t,s,0,inf,0);//變成無匯源
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
add(s,i,0,inf,x+mx);
}
for(int i=1;i<=n;i++){
int tt,x,y;
add(i,i+n,1,inf,0);//下界大於一
add(i+n,t,0,inf,0);
scanf("%d",&tt);
while(tt--){
scanf("%d%d",&x,&y);
add(i+n,x,0,inf,y);
}
}
for(int i=1;i<=t;i++)if(d[i]>0)F.addedge(S,i,d[i],0);else if(d[i]<0)F.addedge(i,T,-d[i],0);
cout<<F.mcmf()%mx<<endl;
}