题目大意
n 个点 m 条边的带权无向图。
你需要构造一个排列,收益为(Sigma_{i=2}^n mincut(a_{i-1},a_i)) 。
(mincut(S,T)) 表示图中 S 为源点,T 为汇点的最小割。
求最大的收益,并输出方案
解析
显然,我们需要先把最小割树建出来,然后,问题就转化为了求排列A使得相邻两数对应的点之间的路径上最小的边之和最大。
考虑贪心,最好情况下会是树上的每一条边都被选中了一次,否则只会使更小的边被选择更多次。那么,如何构造满足要求的排列成为了最大问题。边权小的边肯定要后选择,不然就会导致小的边多次被选。所以,先在树上找到最小的边,标记这条边已经被选过。可以发现,这条边把树分成了两棵子树。对于每一棵子树递归处理,在该子树范围内同样找到最小的边。当一个子树只剩下一个点时,输出该点并结束当前递归。这样可以避免重复输出。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 202
#define M 1002
using namespace std;
const int inf=1<<30;
struct Edge{
int u,v,w;
}e[M];
int head[N],ver[M*4],nxt[M*4],cap[M*4],l;
int head1[N],ver1[N*2],nxt1[N*2],edge1[N*2],l1;
int n,m,i,a[N],tmp1[N],tmp2[N],dis[N],S,T,ans,minx=inf,s,t,id,poi[N],num;
bool use[N*2],vis[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y,int z)
{
ver[l]=y;
cap[l]=z;
nxt[l]=head[x];
head[x]=l;
l++;
ver[l]=x;
cap[l]=0;
nxt[l]=head[y];
head[y]=l;
l++;
}
void insert1(int x,int y,int z)
{
ver1[l1]=y;
edge1[l1]=z;
nxt1[l1]=head1[x];
head1[x]=l1;
l1++;
}
bool bfs()
{
queue<int> q;
memset(dis,-1,sizeof(dis));
q.push(S);
dis[S]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=head[x];i!=-1;i=nxt[i]){
int y=ver[i];
if(dis[y]==-1&&cap[i]>0){
dis[y]=dis[x]+1;
q.push(y);
}
}
}
return (dis[T]>0);
}
int dfs(int x,int flow)
{
if(x==T||flow==0) return flow;
int ans=0;
for(int i=head[x];i!=-1;i=nxt[i]){
int y=ver[i];
if(dis[y]==dis[x]+1&&cap[i]>0){
int a=dfs(y,min(flow,cap[i]));
flow-=a;
ans+=a;
cap[i]-=a;
cap[i^1]+=a;
}
if(flow==0) break;
}
if(flow) dis[x]=-inf;
return ans;
}
int Dinic()
{
int ans=0;
memset(head,-1,sizeof(head));
l=0;
for(int i=1;i<=m;i++){
insert(e[i].u,e[i].v,e[i].w);
insert(e[i].v,e[i].u,e[i].w);
}
while(bfs()) ans+=dfs(S,inf);
return ans;
}
void build(int l,int r)
{
if(l==r) return;
S=a[l],T=a[l+1];
int ans=Dinic(),cnt1=0,cnt2=0;
insert1(S,T,ans);
insert1(T,S,ans);
for(int i=l;i<=r;i++){
if(dis[a[i]]==-1) tmp2[++cnt2]=a[i];
else tmp1[++cnt1]=a[i];
}
for(int i=l;i<=l+cnt1-1;i++) a[i]=tmp1[i-l+1];
for(int i=l+cnt1;i<=r;i++) a[i]=tmp2[i-l-cnt1+1];
build(l,l+cnt1-1);
build(l+cnt1,r);
}
void cal(int x,int pre)
{
for(int i=head1[x];i!=-1;i=nxt1[i]){
int y=ver1[i];
if(y!=pre){
ans+=edge1[i];
if(edge1[i]<minx) minx=edge1[i],s=x,t=y,id=i;
cal(y,x);
}
}
}
int my_comp(const int &x,const int &y)
{
return edge1[x]>edge1[y];
}
void find(int x,int pre)
{
for(int i=head1[x];i!=-1;i=nxt1[i]){
int y=ver1[i];
if(y!=pre&&!vis[i]){
if(edge1[i]<minx) minx=edge1[i],s=x,t=y,id=i;
find(y,x);
}
}
}
void print(int x)
{
minx=inf,s=t=0;
find(x,0);
if(s==0){
poi[++num]=x;
return;
}
int p=s,q=t;
vis[id]=vis[id^1]=1;
print(p);print(q);
}
int main()
{
memset(head1,-1,sizeof(head1));
n=read();m=read();
for(i=1;i<=m;i++) e[i].u=read(),e[i].v=read(),e[i].w=read();
for(i=1;i<=n;i++) a[i]=i;
build(1,n);
cal(1,0);
printf("%d
",ans);
int l=s,r=t;
vis[id]=vis[id^1]=1;
print(l);print(r);
for(i=1;i<=n;i++) printf("%d ",poi[i]);
puts("");
return 0;
}