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;
}

猜你喜欢

转载自www.cnblogs.com/nofind/p/12109276.html
今日推荐