zoukankan      html  css  js  c++  java
  • luoguP4553 80人环游世界

    题意

    这题主要要处理两个限制:
    1.最多有(m)个人,即最多(m)条路径。
    2.每个点(i)的经过次数是给定的,为(a_i)

    其中2.可以用限定上下界的方法解决,1.在建图中讲。

    建图:

    新建三个点:(S'),(T'),(S''),将每个点(i)拆成入点(i)和出点(i')

    1.从(S'')向每个入点连容量为([0,inf])费用为(0)的边。
    2.从每个出点向(T')连容量为([0,inf])费用为(0)的边。
    3.从(S')(S'')连容量为([0,m])费用为(0)的边,这样就解决了限制1。
    4.从每个点的入点向出点连容量为([a_i,a_i])费用为(0)的边。
    5.对于能通行的点对((i,j)(i<j)),从(i)的出点向(j)的入点连边。

    之后我们跑(S'->T')的有源汇上下界最小费用流即为答案。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=210;
    const int inf=1e9;
    int n,m,cnt_edge=1,tot,S,T;
    int head[maxn],dis[maxn],a[maxn],in[maxn],out[maxn];
    int cost[maxn][maxn];
    bool vis[maxn];
    struct Edge{int u,v,down,up,cost;}E[maxn*maxn+maxn*2];
    struct edge{int to,nxt,flow,cost;}e[(maxn*maxn+maxn*4)<<1];
    inline int read()
    {
    	char c=getchar();int res=0,f=1;
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    	return res*f;
    }
    inline void add(int u,int v,int w,int c)
    {
    	e[++cnt_edge].nxt=head[u];
    	head[u]=cnt_edge;
    	e[cnt_edge].to=v;
    	e[cnt_edge].flow=w;
    	e[cnt_edge].cost=c;
    }
    inline void addflow(int u,int v,int w,int c){add(u,v,w,c);add(v,u,0,-c);}
    inline bool spfa()
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        queue<int>q;
        q.push(S);dis[S]=0;vis[S]=1;
        while(!q.empty())
        {
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i;i=e[i].nxt)
            {
                int y=e[i].to;
                if(dis[y]>dis[x]+e[i].cost&&e[i].flow>0)
                {
                    dis[y]=dis[x]+e[i].cost;
                    if(!vis[y])q.push(y),vis[y]=1;
                }
            }
        }
        return dis[T]!=0x3f3f3f3f;
    }
    int dfs(int x,int lim)
    {
        vis[x]=1;
        if(lim<=0||x==T)return lim;
        int res=lim;
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(dis[y]!=dis[x]+e[i].cost||e[i].flow<=0||vis[y])continue;
            int tmp=dfs(y,min(res,e[i].flow));
            res-=tmp;
            e[i].flow-=tmp,e[i^1].flow+=tmp;
            if(res<=0)break;
        }
        return lim-res;
    }
    inline int Dinic(int st,int ed)
    {
    	S=st,T=ed;
        int res=0,cost=0;
        while(spfa())
        {
            int flow=dfs(S,inf);
            res+=flow;cost+=flow*dis[T];
        }
        return cost;
    }
    int main()
    {
    	//freopen("test.in","r",stdin);
    	//freopen("test.out","w",stdout);
    	n=read(),m=read();
    	for(int i=1;i<=n;i++)a[i]=read();
    	for(int i=1;i<=n;i++)
    		for(int j=i+1;j<=n;j++)
    			cost[i][j]=read();
    	E[++tot]=(Edge){2*n+1,2*n+2,0,m,0};//S'=2*n+1,S''=2*n+2,T'=2*n+3.
    	for(int i=1;i<=n;i++)E[++tot]=(Edge){2*n+2,i,0,inf,0},E[++tot]=(Edge){i+n,2*n+3,0,inf,0};	
    	for(int i=1;i<=n;i++)E[++tot]=(Edge){i,i+n,a[i],a[i]};
    	for(int i=1;i<=n;i++)
    		for(int j=i+1;j<=n;j++)
    			if(~cost[i][j])E[++tot]=(Edge){i+n,j,0,inf,cost[i][j]};
    	E[++tot]=(Edge){2*n+3,2*n+1,0,inf,0};
    	for(int i=1;i<=tot;i++)addflow(E[i].u,E[i].v,E[i].up-E[i].down,E[i].cost);
    	for(int i=1;i<=tot;i++)in[E[i].v]+=E[i].down,out[E[i].u]+=E[i].down;
    	for(int i=1;i<=2*n+3;i++)//st=2*n+4,ed=2*n+5.
    		if(in[i]>=out[i])addflow(2*n+4,i,in[i]-out[i],0);
    		else addflow(i,2*n+5,out[i]-in[i],0);
    	printf("%d",Dinic(2*n+4,2*n+5));
    	return 0;
    }
    
  • 相关阅读:
    二分查找
    苹果开发人员账号注冊流程
    cocos2d_android 瞬间动作
    Qt多线程学习:创建多线程
    Java模式(适配器模式)
    代理方法keywordAction与Fun的使用
    装饰者模式
    编写你自己的单点登录(SSO)服务
    4种Java引用浅解
    strtok和strtok_r
  • 原文地址:https://www.cnblogs.com/nofind/p/12109276.html
Copyright © 2011-2022 走看看