携程决赛D题 最小割

最短路径的代价

Problem Description

通常情况下找到N个节点、M 条边的无向图中某两点的最短路径很简单。假设每条边都有各自的长度以及删除这条边的代价,那么给出两个点X和Y,
删除一些边可以使得X到Y的最短路径增加,求删除边最少的代价。

Input

第一行包含参数N和M,分别表示节点和边的个数 (2 ≤N≤ 1000,1 ≤M≤ 11000 )
第二行包含参数X和Y,表示源点和终点( 1 ≤ X,Y ≤ N,X ≠ Y);
剩下M行每行包含4个参数Ui、Vi、Wi和Ci,分别表示节点Ui和Vi之间边的长度Wi以及删除的代价Ci ( 1 ≤ Ui , Vi ≤ N, 0 ≤Wi, Ci ≤1000 );
如果某行N=M=0就表示输入结束。

Output

对于每个用例,按行输出增加X和Y直接最短路径所需要的最小代价。这个代价是所有删除的边的代价之和。

Sample Input

2 3
1 2
1 2 2 3
1 2 2 4
1 2 3 5
4 5
1 4
1 2 1 1
2 3 1 1
3 4 1 1
1 4 3 2
2 2 2 3
4 5
2 3
1 2 3 2
2 4 3 4
1 3 2 3
3 4 2 3
1 4 0 1
0 0

Sample Output

7
3
6

Source

CodingTrip - 携程编程大赛 (决赛)


解题思路:
这道题主要的思路是先求最短路,把所有的最短路在图中抠出来然后建一条以C为容量的容量网络。然后求
最小割。
点击打开链接 提交地址
const  int  inf = 1000000000 ;
const  int  maxn = 20000 , maxm = 500000 ;
struct Edge{
       int v , f ,next ;
       Edge(){}
       Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}
};
int  sourse , meet ;
int  id ;
Edge e[maxm*2 + 10] ;
int  g[maxn + 10] ;

void  add(int u , int v , int f){
      e[++id] = Edge(v , f ,g[u]) ;
      g[u] = id ;
      e[++id] = Edge(u , 0 , g[v]) ;
      g[v] = id ;
}

queue <int> que ;
bool vis[maxn + 10] ;
int  dist[maxn + 10] ;

void bfs(){
     memset(dist , 0 , sizeof(dist)) ;
     while(! que.empty()) que.pop() ;
     que.push(sourse) ;
     vis[sourse] = 1 ;
     while(! que.empty()){
          int u = que.front() ;  que.pop() ;
          for(int i = g[u] ; i ; i = e[i].next){
               int v = e[i].v ;
               if(e[i].f && !vis[v]){
                     que.push(v) ;
                     dist[v] = dist[u] + 1 ;
                     vis[v] = 1 ;
               }
          }
     }
}

int  dfs(int u , int delta){
     if(u == meet) return delta ;
     int ans = 0 ;
     for(int i = g[u] ; i && delta ; i = e[i].next){
           int  v = e[i].v ;
           if(e[i].f && dist[v] == dist[u] + 1){
                int d = dfs(v , min(delta , e[i].f)) ;
                e[i].f -= d ;
                e[i^1].f += d ;
                delta -= d ;
                ans += d ;
           }
     }
     return ans ;
}

int  maxflow(){
     int ans = 0 ;
     while(1){
         memset(vis , 0 , sizeof(vis)) ;
         bfs() ;
         if(! vis[meet]) return ans  ;
         ans += dfs(sourse , inf) ;
     }
}

void init(){
     memset(g , 0 , sizeof(g)) ;
     id = 1 ;
}

struct E{
       int v , w , c ;
       E(){}
       E(int _v , int _w , int _c):v(_v) , w(_w) , c(_c){}
};
vector<E> lis[1008] ;
bool  in[1008] ;
int   mind[1008] ;
void  spfa(int s){
      memset(in , 0 , sizeof(in)) ;
      memset(mind , 63 , sizeof(mind)) ;
      queue<int> q ;
      q.push(s) ;
      in[s] = 1 ;
      mind[s] = 0 ;
      while(! q.empty()){
           int u = q.front() ; q.pop() ;
           in[u] = 0 ;
           for(int i = 0 ; i < lis[u].size() ; i++){
               int v = lis[u][i].v ;
               int w = lis[u][i].w ;
               if(mind[u] + w < mind[v]){
                    mind[v] = mind[u] + w ;
                    if(! in[v]){
                            q.push(v) ; in[v] = 1 ;
                    }
               }
           }
      }
}

int main(){
    int n , m  , t , T = 1 , i , j  ,u , v , w  , c ;
    while(cin>>n>>m){
         if(n == 0 && m == 0)  break ;
         cin>>sourse>>meet ;
         init() ;
         for(i = 1 ; i <= n ; i++) lis[i].clear() ;
         while(m--){
              scanf("%d%d%d%d" ,&u ,&v , &w , &c) ;
              lis[u].push_back(E(v , w , c)) ;
              lis[v].push_back(E(u , w , c)) ;
         }
         spfa(sourse) ;
         for(u = 1 ; u <= n ; u++){
              for(i = 0 ; i < lis[u].size() ; i++){
                 v = lis[u][i].v ;
                 w = lis[u][i].w ;
                 c = lis[u][i].c ;
                 if(mind[u] + w == mind[v])
                     add(u , v , c) ;
              }
         }
         printf("Case %d: %d\n" , T++ , maxflow()) ;
    }
    return 0 ;
}







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值