前言
同学们读题一定要仔细我读错了四遍。
题意
M M M 个人想尽一切办法让 N N N 个国家仅有 V i V_i Vi 个人拜访,同时要满足费用最小。
建图
提前定义一下 ( u , v , d o w n , u p , m o n e y ) (u,v,down,up,money) (u,v,down,up,money) 为 u u u 向 v v v 连下界为 d o w n down down 上界为 u p up up 费用为 m o n e y money money 的边。源点为 s s s,汇点为 t t t。附加源点为 s s ss ss,附加汇点 t t tt tt。
显然有两个地方我们需要拆点:
- 每个人的开始地点。
- 国家与国家相连的边。
这两个地方我们不能保证一定小于或等于他们的定值,所以需要将拆成一个入点( a ′ a' a′)一个出点 ( a a a)。
由“他们主要分布在东方” 中“分布” 一词可得出发点不一样所以 ( s ′ , s , 0 , m , 0 ) (s',s,0,m,0) (s′,s,0,m,0)、 ( s , i ′ , 0 , i n f , 0 ) (s,i',0,inf,0) (s,i′,0,inf,0)、 ( i , t , 0 , i n f , 0 ) (i,t,0,inf,0) (i,t,0,inf,0)。
由“有且仅有 V i V_i Vi 个人会经过那一个国家” 可得 ( i ′ , i , V i , V i , 0 ) (i',i,V_i,V_i,0) (i′,i,Vi,Vi,0)(这样便于不用想其他方式表示它)。
由“打算通过坐飞机” 可得每个国家连边为 ( i , ( i + j ) ′ , 0 , i n f , m o n e y i ) (i,(i+j)',0,inf,money_i) (i,(i+j)′,0,inf,moneyi)。
然后跑一个有上下界最小费用最大流,就做完了。
代码
#include<bits/stdc++.h>
#define int long long
#define inf 2147483647
#define N 112345
using namespace std;
int n , m , s , t , ss , tt , s_ , head[N] , temp_head[N] , now[N] , dep[N] , cnt=0 , money , v[N] ;
bool f[N] ;
struct node{
int to , next , w , c ;
}e[N<<2],temp[N<<2];
void use(int u,int v,int w,int c){
e[cnt].to = v ;
e[cnt].w = w ;
e[cnt].c = c ;
e[cnt].next = head[u] ;
head[u] = cnt++ ;
}
void newnet(int u,int v,int w,int c){
use(u,v,w,c) ;
use(v,u,0,-c) ;
}
bool bfs(){
for(int i=1;i<=n*3;i++) now[i] = head[i] , f[i] = 0 , dep[i] = inf ;
queue<int> q ;
q.push(s) ;
dep[s] = 0 ;
while(!q.empty()){
int u=q.front() ;
q.pop() ;
f[u] = 0 ;
for(int i=head[u];~i;i=e[i].next){
int x=e[i].to ;
if(dep[u]+e[i].c<dep[x]&&e[i].w){
dep[x] = dep[u]+e[i].c ;
if(!f[x]){
f[x] = 1 ;
q.push(x) ;
}
}
}
}
return dep[t]!=inf ;
}
int dfs(int u,int sum){
if(u==t) return sum ;
if(f[u]) return 0 ;
f[u] = 1 ;
int use=0 ;
for(int i=now[u];~i;i=e[i].next){
now[u] = i ;
int x=e[i].to ;
if(!e[i].w||dep[u]+e[i].c!=dep[x]) continue ;
int temp=dfs(x,min(sum,e[i].w)) ;
if(!temp) continue ;
e[i].w -= temp ;
e[i^1].w += temp ;
use += temp ;
money += e[i].c*temp ;
sum -= temp ;
if(!sum) break ;
}
return use ;
}
int dinic(){
int ans=0 ;
while(bfs()) ans += dfs(s,inf) ;
return ans ;
}
signed main(){
memset(head,-1,sizeof head) ;
cin >> n >> m ;
s = n*2+1 ;t = s+1 ;ss = t+1 ;tt = ss+1 ; s_ = tt+1 ;
newnet(s,s_,m,0) ;
for(int i=1;i<=n;i++) scanf("%lld",v+i) ;
for(int i=1;i<=n;i++) newnet(i+n,tt,v[i],0) , newnet(ss,i,v[i],0) , newnet(s_,i+n,m,0) ;
for(int i=1;i<=n;i++){
int u ;
newnet(i,t,inf,0) ;
for(int j=1;j<=n-i;j++){
scanf("%lld",&u) ;
if(u==-1) continue ;
newnet(i,j+i+n,inf,u) ;
}
}
newnet(t,s,inf,0) ;
swap(ss,s) ;
swap(tt,t) ;
dinic() ;
swap(ss,s) ;
swap(tt,t) ;
cout << money << endl ;
return 0 ;
}