2019杭电多校Day1 1005 && hduoj6582 path 最短路建图求最小割&&最大流

Path
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 2115    Accepted Submission(s): 583


Problem Description
Years later, Jerry fell in love with a girl, and he often walks for a long time to pay visits to her. But, because he spends too much time with his girlfriend, Tom feels neglected and wants to prevent him from visiting her.
After doing some research on the neighbourhood, Tom found that the neighbourhood consists of exactly n houses, and some of them are connected with directed road. To visit his girlfriend, Jerry needs to start from his house indexed 1 and go along the shortest path to hers, indexed n. 
Now Tom wants to block some of the roads so that Jerry has to walk longer to reach his girl's home, and he found that the cost of blocking a road equals to its length. Now he wants to know the minimum total cost to make Jerry walk longer.
Note, if Jerry can't reach his girl's house in the very beginning, the answer is obviously zero. And you don't need to guarantee that there still exists a way from Jerry's house to his girl's after blocking some edges.
 

Input
The input begins with a line containing one integer T(1≤T≤10), the number of test cases.
Each test case starts with a line containing two numbers n,m(1≤n,m≤10000), the number of houses and the number of one-way roads in the neighbourhood.
m lines follow, each of which consists of three integers x,y,c(1≤x,y≤n,1≤c≤109), denoting that there exists a one-way road from the house indexed x to y of length c.
 

Output
Print T lines, each line containing a integer, the answer.
 

Sample Input
1
3 4
1 2 1
2 3 1
1 3 2
1 3 3
 

Sample Output
3

思路:

我们先跑一遍dijkstra最短路,然后把所有最短路径上的边抠出来建图,这时我们只需求出新图的最小割即可,求最大流即最小割

但是怎样才能抠出新图呢,我们可以跑两遍最短路,一遍正向最短路假设存为dis,一遍反向最短路存为fdis,如果 u 与 v 之间有一条权值为w的边并且dis[ u ] + fdis[ v ]  + w == dis[ n ],那么边{ u ,v ,w}一定是最短路上的边,于是我们存入新图,但是这样我们要跑两遍复杂度,很麻烦,其实一遍就行了,假设 1 <= i <= n,i 有一条权值为w的边指向 j ,并且                            dis[ i ] + w = dis[ j ],那么边{ i , j , w} 可能在最短路上,这样抠出来的图虽然有多余边,不过当 i == n时,是一定不会把更长的路径上的最后一条边加到新图上的

代码:(EK)

//EK算法求解最大流
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll inf = 34338315071127552;
int n,m,s,t;
struct Node{
    ll v;
    ll val;
    ll next;
}node[20101];
int top=1,head[10101];//top必须从一个奇数开始,一般用-1但我不习惯,解释见下方
void init(int n)
{
    memset(head,-1,sizeof(int)*(n+1));
    top = 1;
}
inline void addedge(ll u,ll v,ll val){
    node[++top].v=v;
    node[top].val=val;
    node[top].next=head[u];
    head[u]=top;
}

inline int Read(){
    int x=0;
    char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x;
}
ll inque[10101];//点是访问过里
struct Pre{
    ll v;//该点的前一个点(从起点过来)
    ll edge;//与该点相连的边(靠近起点的)
}pre[10101];
inline bool bfs(){
    queue<ll>q;
    memset(inque,0,sizeof(inque));
    memset(pre,-1,sizeof(pre));
    inque[s]=1;
    q.push(s);
    while(!q.empty()){
        ll u=q.front();
        q.pop();
        for(int i=head[u];i;i=node[i].next){
            ll d=node[i].v;
            if(!inque[d]&&node[i].val){//node[i].val==0则已经该路径满了
            pre[d].v=u;
            pre[d].edge=i;
            if(d==t)return 1;
            inque[d]=1;
            q.push(d);
            }
        }
    }
    return 0;
}//是否有增广路
ll EK(){
    ll ans=0;
    while(bfs()){
        ll mi=inf;
        for(int i=t;i!=s;i=pre[i].v){
            mi=min(mi,node[pre[i].edge].val);//每次只能增加增广路上最小的边的权值
        }
        for(int i=t;i!=s;i=pre[i].v){
            node[pre[i].edge].val-=mi;
            node[pre[i].edge^1].val+=mi;
            //反向的边的编号是正向边的编号^1
            //这就是为什么top开始时必须是奇数
        }
        ans+=mi;
    }
    return ans;
}
const int maxn = 1e4 + 10;
typedef pair<ll,ll> pii;
vector<pii> G[maxn];
ll dis[maxn];
void djk(int n)
{
    for (int i = 0;i <= n+1;i ++) dis[i] = inf;
    priority_queue<pii,vector<pii>,greater<pii> > que;
    dis[1] = 0;
    que.push({0,1});
    while (!que.empty())
    {
        pii p = que.top();
        ll v = p.second;
        que.pop();
        if (dis[v] < p.first) continue;
        for (int i = 0;i < G[v].size();i ++)
        {
            pii p2 = G[v][i];
            if (dis[p2.first] > dis[v] + p2.second)
            {
                dis[p2.first] = dis[v] + p2.second;
                que.push({dis[p2.first],p2.first});
            }
        }
    }
}
int main(){
/*
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
*/
    int num;
    scanf("%d",&num);
    while (num --)
    {
        //int n,m;
        scanf("%d %d",&n,&m);
        for (int i = 1;i <= n;i ++)
            G[i].clear();
        init(n);
        for (int i = 1;i <= m;i ++)
        {
            ll u,v,w;
            scanf("%lld %lld %lld",&u,&v,&w);
            G[u].push_back({v,w});
        }
        djk(n);
        m = 0;
        for (int i = 1;i <= n;i ++)
        {
            for (int j = 0;j < G[i].size();j ++)
            {
                pii p = G[i][j];
                if (dis[i] + p.second == dis[p.first])
                {
                    //cout<<"u\t"<<i<<"\tv\t"<<p.first<<"\tw\t"<<p.second<<endl;
                    addedge(i,p.first,p.second);
                    addedge(p.first,i,0);
                    m ++;
                }
            }
        }
        s = 1,t = n;
        ll ans = EK();
        printf("%lld\n",ans);
        if (!ans) cout<<"dis\t"<<dis[n]<<endl;
    }
    return 0;
}
//7 8
//1 2 2
//1 3 1
//2 4 1
//3 4 2
//4 5 1
//4 6 2
//5 7 1
//6 7 1

Dinic求最小割:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
//Dinic网络最大流模板
const ll inf=1LL*1<<60;
const int maxn = 10101;
int dep[10101],head[10101],inque[10101];
int top=1;
ll maxflow=0;
int n,m,s,t;
struct Node{
    int v;
    ll val;
    int next;
}node[200100];
void init()
{
    memset(head,-1,sizeof(int)*(n+1));
    top = 1;
}
inline void addedge(int u,int v,ll val){
    node[++top].v=v;
    node[top].val=val;
    node[top].next=head[u];
    head[u]=top;
}
 int Read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0')c=getchar(),f=-1;
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x*f;
}
bool bfs(){
    memset(dep,0x3f,sizeof(int)*(n+1));
    memset(inque,0,sizeof(inque));
    dep[s]=0;
    queue<int>q;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        inque[u]=0;
        for(int i=head[u];~i;i=node[i].next){
            int d=node[i].v;
            if(dep[d]>dep[u]+1&&node[i].val){
                dep[d]=dep[u]+1;//注意与EK的区别
                if(inque[d]==0){
                q.push(d);
                inque[d]=1;
                }
            }
        }
    }
    if(dep[t]!=0x3f3f3f3f)return 1;
    return 0;
}//给增广路上的点分层
ll dfs(int u,ll flow){
    ll rlow=0;
    if(u==t){
        maxflow+=flow;//其实可以直接在这里累加最大流
        return flow;
    }
    ll used=0;//该点已经使用的流量
    for(int i=head[u];i;i=node[i].next){
        int d=node[i].v;
        if(node[i].val&&dep[d]==dep[u]+1){
        if(rlow=dfs(d,min(flow-used,node[i].val))){
            used+=rlow;//该点使用的流量增加
            node[i].val-=rlow;
            node[i^1].val+=rlow;
            if(used==flow)break;//该点流量满了,没必要再找了
        }
        }
    }
    return used;//返回该点已使用流量
}//多路寻找增广路
ll Dinic(){
    while(bfs()){
            dfs(s,inf);
    }
    return maxflow;
}//Dinic寻找最大流
typedef pair<int,ll> pii;
vector<pii> G[10101];
ll dis[maxn];
void djk()
{
    for (int i = 0;i <= n;i ++) dis[i] = inf;
    dis[1] = 0;
    priority_queue<pii> que;
    que.push({1,0});
    while (!que.empty())
    {
        pii p = que.top();
        que.pop();
        if (dis[p.first] < p.second) continue;
        for (int i = 0;i < G[p.first].size();i ++)
        {
            pii p2 = G[p.first][i];
            if (dis[p2.first] > dis[p.first] + p2.second)
            {
                dis[p2.first] = dis[p.first] + p2.second;
                que.push({p2.first,dis[p2.first]});
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
//    freopen("6582in.txt","r",stdin);
//    freopen("6582out.txt","w",stdout);
    int x,u,v,w;
    cin>>x;
    while (x --)
    {
        cin>>n>>m;
        init();
        for (int i = 0;i <= n+10;i ++) G[i].clear();
        while (m --)
        {
            cin>>u>>v>>w;
            G[u].push_back({v,w});
        }
        djk();
        m = 0;
        for (int i = 1;i <= n;i ++)
        {
            for (int j = 0;j < G[i].size();j ++)
            {
                pii p = G[i][j];
                if (dis[i] + p.second == dis[p.first])
                {
                    addedge(i,p.first,p.second);
                    addedge(p.first,i,0);
                    m++;
                }
            }
        }
        s = 1,t = n;
        maxflow = 0;
        cout<<Dinic()<<'\n';
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值