zoukankan      html  css  js  c++  java
  • JZOJ 100003. 【NOI2017模拟.4.1】 Tree(费用流)

    JZOJ 100003. 【NOI2017模拟.4.1】 Tree

    题目

    Description

    在这里插入图片描述

    Input

    在这里插入图片描述

    Output

    在这里插入图片描述

    Sample Input

    1
    5 3
    1 2 1
    1 4 1
    2 3 1
    2 5 1
    1 3 2
    1 5 4
    1 4 3

    Sample Output

    7

    Data Constraint

    在这里插入图片描述

    题解

    • 这题单纯从树的方面考虑的确没什么想法,因为正解是要连边跑最小费用最大流。
    • 做法其实很简单:
    • 首先设源点 S S S和汇点 T T T,令每条路径上深度大的为起点,深度小的为终点
    • 每条树边上从深度大的点向深度小的连边,容量就是边的最大覆盖次数,费用为 0 0 0
    • 每条路径上从起点向终点连边,容量为 1 1 1,费用为路径的收益,
    • 源点向每条路径的起点连边,每条路径的终点向汇点连边,容量为 1 1 1,费用为 0 0 0
    • 不难发现,这样子的最大流一定是路径的总数 m m m,那么最小费用代表什么呢?
    • 可以这样理解,首先认为每条路径都是要选的,收益总和为 s u m sum sum
    • 如果直接从路径的起点不经树边到达终点,相当于这条路径不选,需要花费收益(相当于减去了收益),不占用树边的容量,
    • 如果经过树边到达终点,相当于这条路径选,不需要花费收益,占用了树边的容量。
    • s u m sum sum减去费用 s s s,剩下的就是所有选出的边的总收益,
    • 因为 s s s是最小费用,所以 s u m − s sum-s sums是最大收益。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 10010
    #define ll long long
    int last[N],nxt[3*N],to[3*N],len;
    int dp[N],S,T,n,m;
    int p[N],q[N],bz[N];
    ll dis[N],ans;
    struct
    {
       int x,y,w;
    }a[N];
    struct
    {
       int to,w;
       ll c;
    }e[N*3];
    void add(int x,int y)
    {
       to[++len]=y;
       nxt[len]=last[x];
       last[x]=len;
    }
    void add1(int x,int y,int w,ll c)
    {
       e[++len].to=y;
       e[len].w=w;
       e[len].c=c;
       nxt[len]=last[x];
       last[x]=len;
    }
    void dfs(int x,int fa)
    {
       for(int i=last[x];i;i=nxt[i]) if(to[i]!=fa)
       {
       	dp[to[i]]=dp[x]+1;
       	dfs(to[i],x);
       }
    }
    int id=0;
    int SPFA()
    {
       dis[S]=0,p[S]=1,q[1]=S;
       bz[S]=++id;
       int l=0,r=1;
       while(l!=r)
       {
       	l=l%(n+5)+1;
       	int x=q[l];
       	for(int i=last[x];i;i=nxt[i]) if(e[i].w)
       	{
       		int y=e[i].to;
       		if(dis[x]+e[i].c<dis[y]||bz[y]!=id)
       		{
       			bz[y]=id;
       			dis[y]=dis[x]+e[i].c;
       			if(!p[y])
       			{
       				r=r%(n+5)+1;
       				q[r]=y;
       				p[y]=1;
       			}
       		}
       	}
       	p[x]=0;
       }
       return dis[T]<1e+16&&bz[T]==id;
    }
    int count(int k,int flow)
    {
       if(k==T) return flow;
       int have=0;
       p[k]=1;
       for(int i=last[k];i;i=nxt[i]) if(e[i].w)
       {
       	int x=e[i].to;
       	if(!p[x]&&dis[k]+e[i].c==dis[x])
       	{
       		int t=min(flow-have,e[i].w);
       		int now=count(x,t);
       		ans-=e[i].c*now;
       		have+=now,e[i].w-=now,e[i^1].w+=now;
       		if(have==flow) break;
       	}
       }
       p[k]=0;
       return have;
    }
    int main()
    {
       int tn;
       scanf("%d",&tn);
       while(tn--)
       {
       	int i,x,y;ll c;
       	scanf("%d%d",&n,&m);	
       	memset(last,0,sizeof(last));
       	len=0;
       	for(i=1;i<n;i++) 
       	{
       		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
       		add(a[i].x,a[i].y),add(a[i].y,a[i].x);
       	}
       	dp[1]=1;
       	dfs(1,0);
       	memset(last,0,sizeof(last));
       	len=1;
       	for(i=1;i<n;i++)
       	{
       		if(dp[a[i].x]>dp[a[i].y]) swap(a[i].x,a[i].y);
       		add1(a[i].y,a[i].x,a[i].w,0);
       		add1(a[i].x,a[i].y,0,0);
       	}
       	S=n+1,T=n+2,ans=0;
       	for(i=1;i<=m;i++)
       	{
       		scanf("%d%d%lld",&x,&y,&c);
       		ans+=c; 
       		if(dp[x]>dp[y]) swap(x,y);
       		add1(S,y,1,0),add1(y,S,0,0);
       		add1(y,x,1,c),add1(x,y,0,-c);
       		add1(x,T,1,0),add1(T,x,0,0);
       	}
       	while(SPFA()) while(count(S,1e+6));
       	printf("%lld
    ",ans);
       }
       return 0;
    } 
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    DevComponents DotNetBar 中Ribbon汉化
    类型Universe无法解析程序 集:System.Design,Version=2.0.0.0
    MapXtreme使用编辑工具
    html中汉字乱码
    c#皮肤使用
    c# 中的internal使用
    DonNetBar汉化
    外汇期货学习专帖(转)
    苦难属于悲情的人
    IT规划宜分步走 忌盲目好大喜功(载)
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910047.html
Copyright © 2011-2022 走看看