zoukankan      html  css  js  c++  java
  • HDU 4067 hdoj 4067 Random Maze 最小费用流

    给出n个点,m条边,入口s和出口t,对于每条边有两个值a,b,如果保留这条边需要花费;否则,移除这条边需要花费b。
    题目要求用最小费用构造一个有向图满足以下条件:
    1.只有一个入口和出口
    2.所有路都是唯一方向
    3.对于入口s,它的出度 = 它的入度 + 1
    4.对于出口t,它的入度 = 它的出度 + 1
    5.除了s和t外,其他点的入度 = 其出度
    最后如果可以构造,输出最小费用;否则输出impossibe。
    题解:
    对于所有的边(u,v,a,b)我们先进行保留。
    因为题目要求3和4.我们虚拟添加一条边从t到s in[s]++;out[t]++.
    然后得到一个图,这个图必须经过修改使得所有点出度=入度。
    和hdu3488有点类似,但是那题是在有给定明确的出度入度的时候才能拆点跑费用流。
    于是注意到Σin[i]=Σout[i],所以得出Σin[i]-out[i] (i|{in{i]>out[i]}=Σout[j]-in[j] (i|{out{j]>in[j]})
    因此将所有的点集合分为两个集合, 一部分是(i|{in{i]>out[i]}一部分是(j|{out{j]>in[j]})
    于是对于每个点i, 如果in[i] > out[i] . 建边S->i, 权值为0, 流量为in[i] – out[i];
    否则的话 建边 i->T ,权值为0, 流量为out[i] – in[i];
    然后因为我们要使他们的in[i]-out[i]为0,因此只需要通过将原来的边删除,减少出度和入度即可。
    跑一遍最小费用流即可。但是因为负权边会使spfa的效率降低,因此建边时略有不同,所以采用了第二种建边方式。看代码即可。
    傻逼博客园代码看起来好搓
    const int maxn=262144,INF=999999999;
    int next[maxn],last[maxn],tot,e[maxn],op[maxn],cost[maxn],c[2010][2010],b[2010],a[2010],dist[maxn],inq[maxn],from[maxn];
    int val[maxn],S,n,m,T,q[100maxn],ans,u[maxn],in[maxn],out[maxn],total;
    void add(int x,int y,int v,int c)
    {
    next[++tot]=last[x];last[x]=tot;val[tot]=v;e[tot]=y;op[tot]=tot+1;cost[tot]=c;u[tot]=x;
    next[++tot]=last[y];last[y]=tot;e[tot]=x;op[tot]=tot-1;val[tot]=0;cost[tot]=-c;u[tot]=y;
    }
    int spfa()
    {
    for (int i=S;i<=T;i++)dist[i]=INF;
    dist[S]=0;
    int h=0,t=0;
    q[++t]=S;inq[S]=0;
    while (h<t)
    {
    int x=q[++h];
    for (int i=last[x];i;i=next[i])
    {
    int v=e[i];
    if (val[i]&&dist[v]>dist[x]+cost[i])
    {
    from[v]=i;
    dist[v]=dist[x]+cost[i];
    if (!inq[v])inq[v]=1,q[++t]=v;
    }
    }
    inq[x]=0;
    }
    if(dist[T]==INF)return 0;return 1;
    }
    void mcf()
    {
    int x=INF,i=T;
    for (int i=from[T];i;i=from[u[i]])
    x=std::min(x,val[i]);
    for (int i=from[T];i;i=from[u[i]])
    {
    val[i]-=x;
    val[op[i]]+=x;
    ans+=x
    cost[i];
    }
    total=total-x;
    }
    int main()
    {
    int u,v,a,b,sum,cases,s,t;
    scanf("%d",&cases);
    for (int j=1;j<=cases;j++)
    {
    scanf("%d%d%d%d",&n,&m,&s,&t);
    tot=sum=0;
    std::memset(last,0,sizeof(last));
    std::memset(in,0,sizeof(in));
    std::memset(out,0,sizeof(out));
    for (int i=1;i<=m;i++)
    {
    scanf("%d%d%d%d",&u,&v,&a,&b);
    if (a<b)
    {
    out[u]++;in[v]++;
    add(v,u,1,b-a);
    } else add(u,v,1,a-b);
    sum+=std::min(a,b);
    }
    in[s]++;out[t]++;
    S=0;T=n+1;total=0;
    for (int i=1;i<=n;i++)
    if (in[i]>out[i])
    add(S,i,in[i]-out[i],0),total+=in[i]-out[i];
    else
    if (in[i]<out[i])add(i,T,out[i]-in[i],0);
    ans=0;
    while(spfa())
    mcf();
    printf("Case %d: ",j);
    if (!total)printf("%d ",ans+sum);
    else printf("impossible ");
    }
    return 0;
    }

  • 相关阅读:
    20191330雷清逸 学习笔记4
    sort
    20191330雷清逸 MyOD(选作,计入平时成绩)
    20191330雷清逸 Linux C语言编程基础(必做)
    20191330雷清逸 学习笔记3
    20191330雷清逸 学习笔记2
    无限的技能
    20191330 雷清逸 学习笔记1
    2021-2022-1-diocs-Linux系统编程第四周学习笔记
    Linux下man命令的使用
  • 原文地址:https://www.cnblogs.com/wkingG/p/5112384.html
Copyright © 2011-2022 走看看