传送门

表示完全看不懂最小费用可行流……

据某大佬说

我们考虑拆点,然后进行如下连边

$s$向$a_i$连边,权值$0$,容量$[0,m]$

$a_i$向$a_i'$连边,权值$0$容量$[v_i,v_i]$

如果存在边$(i,j)$,则连边$a_i'->a_i$,权值为$w_{i,j}$,容量$[0,m]$

$a_i'$向$t$连边,权值$0$,容量$[0,m]$

$t$向$t'$连边,权值$0$容量$[0,m]$

然后跑个最小费用可行流

 //minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
const int N=,M=;
int head[N],Next[M],ver[M],edge[M],flow[M],tot=;
inline void add(int u,int v,int e,int f){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e,flow[tot]=f;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=-e,flow[tot]=;
}
int dis[N],vis[N],cur[N],S,T,ans,s,t,kt,n,m;
queue<int> q;
bool spfa(){
memset(dis,-,sizeof(dis));
memset(vis,,sizeof(vis));
memcpy(cur,head,sizeof(head));
q.push(T),dis[T]=,vis[T]=;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=;
for(int i=head[u];i;i=Next[i])
if(flow[i^]){
int v=ver[i],e=edge[i];
if(dis[v]<||dis[v]>dis[u]-e){
dis[v]=dis[u]-e;
if(!vis[v]) vis[v]=,q.push(v);
}
}
}
return ~dis[S];
}
int dfs(int u,int limit){
if(!limit||u==T) return limit;
int fl=,f;vis[u]=;
for(int i=cur[u];i;cur[u]=i=Next[i]){
int v=ver[i];
if(dis[v]==dis[u]-edge[i]&&!vis[v]&&(f=dfs(v,min(limit,flow[i])))){
fl+=f,limit-=f;
ans+=f*edge[i];
flow[i]-=f,flow[i^]+=f;
if(!limit) break;
}
}
vis[u]=;
return fl;
}
void zkw(){
while(spfa()) dfs(S,inf);
}
int main(){
//freopen("testdata.in","r",stdin);
n=read(),m=read(),s=*n+,t=s+,kt=t+,S=kt+,T=S+;
for(int i=;i<=n;++i){
int x=read();
add(S,i+n,,x),add(i,T,,x),
add(s,i,,m),add(i+n,t,,m);
}
add(t,kt,,m),add(kt,s,,inf);
for(int i=;i<=n;++i)
for(int j=i+;j<=n;++j){
int x=read();
if(~x) add(n+i,j,x,inf);
}
zkw();
printf("%d\n",ans);
return ;
}
05-06 06:49