任务安排
最小费用最大流的应用(我似乎是第一次实践费用流?)。
按照题目的说法,每个人都只能操作一台机器,每台机器也只能被一个人操作,这比较简单,把两类节点分别向源汇点连1边即可。但它要求收益最大,但最长路是不好跑的(即使它是DAG),所以考虑给每条边的边权取一个相反数,这样就可以让问题变成最小费用最大流了。SPFA是可以做负权图哒。
#include<bits/stdc++.h>
//#define feyn
const int N=1010;
const int M=N*N;
const int maxn=1e9;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int min(int s1,int s2){
return s1<s2?s1:s2;
}
int m,n,a[N],b[N],ss,tt,cnt;
struct edge{
int t,v1,v2,next;
}e[M];
int head[N*3],esum=1;
inline void adde(int fr,int to,int v1,int v2){
e[++esum]=(edge){to,v1,v2,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int v1,int v2){
//printf("%d %d %d %d\n",fr,to,v1,v2);
adde(fr,to,v1,v2);adde(to,fr,0,-v2);
}
queue<int>q;
int dis[N];int tot;
bool inq[N],vis[N];
inline bool check(){
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
memset(inq,0,sizeof(inq));
inq[ss]=true;dis[ss]=0;q.push(1);
while(!q.empty()){
int wh=q.front();q.pop();inq[wh]=false;
//printf("%d %d\n",wh,dis[wh]);
for(int i=head[wh],th;i;i=e[i].next){
if(e[i].v1==0)continue;
int now=dis[wh]+e[i].v2;
if(dis[th=e[i].t]<=now)continue;
dis[th]=now;if(inq[th]==false)inq[th]=true,q.push(th);
}
}
return dis[tt]<1e8;
}
int cost;
inline int dinic(int wh,int val){
//printf("%d %d\n",wh,val);
if(wh==tt)return cost+=dis[wh]*val,val;
int used=0;vis[wh]=true;
for(int i=head[wh],th;i;i=e[i].next){
if(vis[th=e[i].t]||dis[th]!=dis[wh]+e[i].v2||e[i].v1==0)continue;
int now=dinic(th,min(val,e[i].v1));
if(now)used+=now,val-=now,e[i].v1-=now,e[i^1].v1+=now;
}
return used;
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);read(n);
ss=++cnt;tt=++cnt;
for(int i=1;i<=m;i++)a[i]=++cnt,b[i]=++cnt;
for(int i=1;i<=m;i++)add(ss,a[i],1,0),add(b[i],tt,1,0);
int s1,s2,s3;
while(~scanf("%d%d%d",&s1,&s2,&s3))add(a[s1],b[s2],1,-s3);
int ans=0;
while(check())ans+=dinic(ss,maxn);
printf("%d %d\n",ans,-cost);
return 0;
}
一如既往,万事胜意