zoukankan      html  css  js  c++  java
  • [CF343E] Pumping Stations

    题目大意

    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;
    }
    
  • 相关阅读:
    zabbix监控nginx的性能
    常用iptables命令
    shell脚本小示例
    打印菜单脚本
    ping主机脚本
    Linux网络配置脚本
    多磁盘自动分区自动挂载脚本
    深入js系列-类型(null)
    深入js系列-类型(开篇)
    first-child、last-child误解
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12194126.html
Copyright © 2011-2022 走看看