HDU - 6582 ( 最短路 + 网络流 )

HDU - 6582  ( 最短路 + 网络流 )

题目链接: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 int long long
#define inf 0x3f3f3f3f

using namespace std;

const int maxn = 2e4+10;
int u[maxn],v[maxn],w[maxn];
int dep[maxn],cur[maxn];
int head[maxn],cnt,n,m,s,t;
int dis[2][maxn],via[maxn];
struct node {
    int to,nxt,w;
}e[maxn];
typedef struct nod {
    int v,date;
} ty;
ty tt,d;

bool operator<( const ty&a, const ty&b )
{
    return a.date>b.date;
}

void addage( int u, int v, int w )
{
    e[cnt].to = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
}

void dijstra( int u, int op )
{
    memset(via,0,sizeof(via));
    priority_queue<ty> Q;
    tt.v = u; tt.date = 0;
    Q.push(tt);
    while ( !Q.empty() ) {
        if ( via[ Q.top().v ]==1 ) {
            Q.pop();
            continue ;
        }
        tt = Q.top(); Q.pop();
        int u = tt.v;
        dis[op][u] = tt.date;
        via[u] = 1;
        for ( int i=head[u]; i!=-1; i=e[i].nxt ) {
            int v = e[i].to,w=e[i].w;
            if ( via[v]==0 && dis[op][v]>dis[op][u]+w ) {
                dis[op][v] = dis[op][u]+w;
                d.v = v; d.date = dis[op][v];
                Q.push(d);
            }
        }
    }
}

int bfs( int node )
{
    memset(dep,0,sizeof(dep));
    memcpy(cur,head,sizeof(cur));
    dep[node] = 1;
    queue<int> Q;
    Q.push(node);
    while ( !Q.empty() ) {
        int x = Q.front();Q.pop();
        for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
            int y = e[i].to,w=e[i].w;
            if ( !dep[y] && w ) {
                dep[y] = dep[x] + 1;
                Q.push(y);
            }
        }
    }
    return dep[t];
}

int dfs( int x, int flow )
{
    if ( x==t ) return flow;
    int sum = 0;
    for ( int i=cur[x]; i!=-1; i=e[i].nxt ) {
        cur[x] = i;
        int y = e[i].to, w=e[i].w;
        if ( w && dep[y]==dep[x]+1 ) {
            int t = dfs(y,min(flow-sum,w));
            sum += t;
            e[i].w -= t;
            e[i^1].w += t;
            if ( sum==flow ) break;
        }
    }
    if ( sum==0 ) dep[x] = 0;
    return sum;
}

signed main()
{
    int T;cin>>T;
    while ( T-- ) {
        cin >> n >> m;
        memset(head,-1,sizeof(head)); cnt = 0;
        memset(dis,inf,sizeof(dis));
        for ( int i=0; i<m; i++ ) {
            scanf("%lld %lld %lld",&u[i],&v[i],&w[i]);
            addage(u[i],v[i],w[i]);
        }
        dijstra(1,0);
        memset(head,-1,sizeof(head)); cnt = 0;
        for ( int i=0; i<m; i++ ) {
            addage(v[i],u[i],w[i]);
        }
        dijstra(n,1);
        memset(head,-1,sizeof(head)); cnt = 0;
        for ( int i=0; i<m; i++ ) {
            if ( dis[0][ u[i] ] + w[i] + dis[1][ v[i] ]==dis[0][n] ) {
                addage(u[i],v[i],w[i]);
                addage(v[i],u[i],0);
            }
        }
        int ans = 0;
        s = 1; t = n;
        while ( bfs(s) ) {
            ans += dfs(s,inf);
        }
        cout << ans << endl;
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值