HDU 6582. Path (Dijkstra+Dinic)

题目: http://acm.hdu.edu.cn/showproblem.php?pid=6582

题意:
给定一张有向图,n个点,m条边;
可以砍掉一些边,砍掉每条边的代价是边的权值;
问最少花费多少,能使得从1到n的最短路长度至少增加1,不连通也算长度增加。
输出最少花费。

分析:
1.总体思路
由于从1到n的最短路可能存在多条,所以需要先把所有最短路上的边拎出来建一张图G;
然后在图G上跑最小割,用最小的花费把源点1和汇点n割开,割开之后原图中所有原有最短路必然不联通,此时1~n的最短路长度必然增加;
因为“最小割=最大流”定理,所以只需要在图G上跑最大流算法即可。

2.建立图G
如何建立图G呢?
这相当于要找出从1到n的所有最短路。
如果在dijkstra中用vector记录每个节点的多个前驱节点(或边),有可能会MLE;
怎么办呢?
观察每一条边,如果其存在于从1到n的最短路上,那么满足式子:
dist[u]+cost[i]+dist2[v]==dist[n],
其中dist[j]是从1到j的最短路,cost[i]是第i条边的权值,dist2[j]是从j到n的最短路;
dist2是通过反向建图跑dijkstra得到的。
为了防止TLE,这里使用Heap+Dijkstra。

3.最大流
Dinic由于采用分层图、多路增广等策略,时间复杂度比其他网络流算法较优,其时间复杂度是O(n^2*m);
算起来仍然TLE,但网络流算法时间复杂度很奇怪,Dinic跑的很快,加上当前弧优化之后交就是了!

代码:

#include <bits/stdc++.h>
#define Pii pair<int,int>
using namespace std;
typedef long long llong;
const llong inf=1e16;
const int tmax=1e4+5,inf_int=1e9+5;
struct node {
    llong d;
    int x;
    bool operator<(const node &b)const
    {
        return d>b.d;
    }
};
int n,m,xx[tmax],yy[tmax],cc[tmax];
vector<Pii> GG[tmax];
priority_queue<node> Q;
bool vis[tmax];
llong dis[tmax],_dis[tmax];
void dijk(int s,llong *d)
{
    int i,u,v,w,len;
    node tmp;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++) d[i]=inf;
    d[s]=0;
    Q.push((node){0,s});
    while(!Q.empty())
    {
        tmp=Q.top();
        Q.pop();
        u=tmp.x;
        if(vis[u]) continue;
        vis[u]=true;
        len=GG[u].size();
        for(i=0;i<len;i++)
        {
            v=GG[u][i].first;
            w=GG[u][i].second;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                Q.push((node){d[v],v});
            }
        }
    }
    return;
}
struct edge{
    int to,cap,rev;
};
vector<edge> G[tmax];
int level[tmax]; 
int iter[tmax];  
void add(int from,int to,int cap)
{
    G[from].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){from,0,G[from].size()-1});
    return;
}
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int> que;
    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int v=que.front();
        que.pop();
        for(int i=0;i<G[v].size();i++)
        {
            edge &e=G[v][i];
            if(e.cap>0&&level[e.to]<0)
            {
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
    return;
}
int dfs(int v,int t,int f)
{
    if(v==t) return f;
    for(int &i=iter[v];i<G[v].size();i++)
    {
        edge &e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to])
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int Dinic(int s,int t)
{
    int flow=0;
    while(1)
    {
        bfs(s);
        if(level[t]<0) return flow;
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf_int))>0)
            flow+=f;
    }
}
int main()
{
    int TT,i;
    scanf("%d",&TT);
    while(TT--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&xx[i],&yy[i],&cc[i]);
            GG[xx[i]].push_back(Pii(yy[i],cc[i]));
        }
        dijk(1,dis);
        for(i=1;i<=n;i++)
            GG[i].clear();
        for(i=1;i<=m;i++)
            GG[yy[i]].push_back(Pii(xx[i],cc[i]));
        dijk(n,_dis);
        for(i=1;i<=m;i++)
        {
            if(dis[xx[i]]+cc[i]+_dis[yy[i]]==dis[n])
                add(xx[i],yy[i],cc[i]);
        }
        cout<<Dinic(1,n)<<endl;
        for(i=1;i<=n;i++)
        {
            GG[i].clear();
            G[i].clear();
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值