UVa10806 - Dijkstra, Dijkstra.(费用流)

题目链接

简介:
给出一个有n个节点,m条边的带权无向图,找到起点S到终点T的最短往返路线不能重复走同一条边

简介:
这道题很像dijkstra求最短路和次短路
但是求解最短路次短路的时候,我们贯彻的是一种贪心思想,并且可以走重边
如果只是简单地把最短路上的边删除,再跑一边最短路,是会出错的
因此这道题就带有一点dp的味道了

既像贪心,又像dp:网络流

这道题的建图很简单,
因为我们要找出最短的两条互不影响的路,所以我们用费用流解决
这样就可以保证找到的一定是全脂和最小的路径

但如果从S到T用多条路怎么办
很简单,我们限制一下流量就好了
设超级源点SS,向S连边,容量为2,费用为0
设超级汇点TT,T向其连边,容量为2,费用为0

只有增广出来的总流量为2时,原问题才有解

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>

using namespace std;

const int INF=0x33333333;
const int N=102;
struct node{
    int x,y,v,c,nxt;
};
node way[N*N*2];
int st[N],tot,n,m,s,t;
int pre[N],dis[N],ans;
bool in[N];

void add(int u,int w,int z,int cc)
{
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].c=cc;way[tot].nxt=st[u];st[u]=tot;
    tot++;
    way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].c=-cc;way[tot].nxt=st[w];st[w]=tot;
}

int spfa(int s,int t)
{
    memset(pre,0,sizeof(pre));
    memset(dis,0x33,sizeof(dis));
    memset(in,0,sizeof(in));

    queue<int> Q;
    Q.push(s);
    dis[s]=0;  in[s]=1;

    while (!Q.empty())
    {
        int now=Q.front(); Q.pop();
        in[now]=0;
        for (int i=st[now];i!=-1;i=way[i].nxt)
            if (way[i].v&&dis[way[i].y]>dis[now]+way[i].c)
            {
                dis[way[i].y]=dis[now]+way[i].c;
                pre[way[i].y]=i;
                if (!in[way[i].y])
                {
                    in[way[i].y]=1;
                    Q.push(way[i].y);
                }
            }
    }
    return dis[t]<INF;
}

int doit(int s,int t)
{
    ans=0;
    int flow=0;
    while (spfa(s,t))
    {
        int sum=INF;
        for (int i=t;i!=s;i=way[pre[i]].x) 
            sum=min(sum,way[pre[i]].v);
        ans+=sum*dis[t]; flow+=sum;
        for (int i=t;i!=s;i=way[pre[i]].x)
            way[pre[i]].v-=sum,
            way[pre[i]^1].v+=sum;
    }
    return flow;
}

int main()
{
    while (scanf("%d",&n)!=EOF&&n)
    {
        memset(st,-1,sizeof(st));
        tot=-1;

        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            int u,w,z;
            scanf("%d%d%d",&u,&w,&z);
            add(u,w,1,z);
            add(w,u,1,z);
        }

        s=0; t=n+1;
        add(s,1,2,0);
        add(n,t,2,0);

        int flow=doit(s,t);
        if (flow==2) printf("%d\n",ans);
        else printf("Back to jail\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值