题目链接:点击这里
#include<iostream>//二分图最大权匹配
#include<cstdio>
#include<queue>
#include<algorithm>
#define N 505
#define M 250005
#define INF 9990365505
#define ll long long
using namespace std;
int n,m,x,y,z,tot,tim;
int pre[N];//记录x的先驱节点
int visx[N],visy[N];//visx[i]表示左侧第i个节点第几轮访问
int matchx[N],matchy[N];//matchy[i]表示左边第i个节点与右边第matchy[i]个节点匹配
struct node{
int to,w,next;
}kkk[M];
int head[N];//链式前向星存边
ll ex[N],ey[N],slack[N];//ex表示左边节点值,ey表示右边节点的值,slack用于维护最小的的点权之和减去边权
void addedge(int x,int y,int z){
//链式前向星存边
tot++;
kkk[tot].to=y;
kkk[tot].w=z;
kkk[tot].next=head[x];
head[x]=tot;
}
void modify(int cur){
//修改之前的 匹配方式
for (int last,x=cur;x;x=last){
last=matchx[pre[x]];
matchx[pre[x]]=x;
matchy[x]=pre[x];
}
}
void bfs(int cur){
for(int i=1;i<=n;i++)
slack[i]=INF,pre[i]=0;//初始化
queue<int>q;
q.push(cur);
++tim;
while(1){
while (!q.empty()){
//bfs交错树
int u=q.front();
q.pop();
visx[u]=tim;//u是第tim轮被访问的
for (int i=head[u];i;i=kkk[i].next){
int v=kkk[i].to,cost=kkk[i].w;//访问u的相邻节点
if(visy[v]==tim)
continue;//本轮已经被访问过的不需要再次被访问
ll mincost=ex[u]+ey[v]-cost;//记录
if (mincost<slack[v]){
//维护最小点权之和减边权
slack[v]=mincost;
pre[v]=u;//v的先驱节点记为u
if (!mincost){
//mincost==0 则连边
visy[v]=tim;//这一轮也访问到了v
if (!matchy[v]){
//左侧第v个节点没有与右侧节点匹配
modify(v);//修改之前的匹配方式,并终止
return;
}
else q.push(matchy[v]);//否则入队
}
}
}
}
ll mincost=INF;
for(int i=1;i<=n;++i){
if(visy[i]!=tim){
//本轮没有被访问过
mincost=min(mincost,slack[i]);//在交错树的边中寻找顶标和与边权之差最小的边
}
}
for(int i=1;i<=n;++i){
if (visx[i]==tim)//左侧节点减去这个值
ex[i]-=mincost;
if (visy[i]==tim)
ey[i]+=mincost;//右侧节点加这个值
else
slack[i]-=mincost;//维护更新
}
for(int i=1;i<=n;++i){
if( visy[i]!=tim && !slack[i]){
//发生冲突并解决冲突之后,继续匹配
visy[i]=tim;//标记为本轮访问的
if (!matchy[i]){
//没有匹配过
modify(i);//修改
return;//返回
}
else q.push(matchy[i]);//否则入队
}
}
}
}
void KM(){
for(int i=1;i<=n;++i)
bfs(i);//对每个点做一遍bfs
//做完之后的匹配值就是左右两端节点数的总和
ll ans=0;//
for (int i=1;i<=n;++i)
ans+=ex[i]+ey[i];
printf("%lld\n",ans);
for (int i=1;i<=n;++i)
printf("%d ",matchy[i]);
printf("\n");
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i){
//读入数据
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);//加边
ex[x]=max(ex[x],(ll)z);//点x的最大权值ex[x]是与它相邻的边的最大的权值
}
KM();//km算法求解
return 0;
}