[BZOJ1266]-[AHOI2006]上学路线route-SPFA+最小割

说在前面

感觉早期的题都是在考算法= =
不过至少比那种把小学奥数拿到NOIP凑数的好hhhhh


题目

BZOJ1266传送门

题面

由于题面实在是太长了,Izumi并不想把一大段文字直接粘过来
这题不是权限题,可以进去看题面


解法

读完题大概都知道要干什么了吧=w=
要删掉一些边使得最短路变长,那就必须割掉所有最短路的某条边。
在此基础上还要费用最小,最小割->最大流->模板题


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , head[505] , Whead[505] , tp , tw = 1 ;
struct Path{
    int pre , to , len , flow ;
}p[250000] , w[250000] ;

inline void In( int t1 , int t2 , int t3 , int t4 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
    p[tp].len = t3 ;
    p[tp].flow = t4 ;
}

inline void WIn( int t1 , int t2 , int t3 ){
    w[++tw].pre = Whead[t1] ;
    w[ Whead[t1] = tw ].to = t2 ;
    w[tw].flow = t3 ;
}

bool inque[505] ;
int dis[505] , que[250005] , fr , ba ;
void SPFA(){
    fr = 125001 , ba = 125000 ;
    memset( dis , 0x3f , sizeof( dis ) ) ;
    dis[1] = 0 ; que[++ba] = 1 ; inque[1] = true ;
    while( ba >= fr ){
        int u = que[fr++] ; inque[u] = false ;
        for( int i = head[u] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            if( dis[v] > dis[u] + p[i].len ){
                dis[v] = dis[u] + p[i].len ;
                if( !inque[v] ){
                    if( dis[v] < dis[ que[fr] ] ) que[--fr] = v ;
                    else que[++ba] = v ;
                    inque[v] = true ;
                }
            }
        }
    }
    printf( "%d\n" , dis[N] ) ;
}

void build(){
    for( int u = 1 ; u <= N ; u ++ )
        for( int i = head[u] ; i ; i = p[i].pre )
            if( dis[u] + p[i].len == dis[ p[i].to ] ){
                WIn( u , p[i].to , p[i].flow ) ;
                WIn( p[i].to , u , 0 ) ;
            }
}

bool Bfs(){
    memset( dis , -1 , sizeof( dis ) ) ;
    dis[1] = 0 ;
    fr = 1 , ba = 0 ; que[++ba] = 1 ;
    while( fr <= ba ){
        int u = que[fr++] ;
        for( int i = Whead[u] ; i ; i = w[i].pre ){
            int v = w[i].to ;
            if( dis[v] != -1 || !w[i].flow ) continue ;
            dis[v] = dis[u] + 1 ;
            que[++ba] = v ;
        }
    }
    return dis[N] != -1 ;
}

int dfs( int u , int flow ){
    if( u == N || !flow ) return flow ;
    int totf = 0 ;
    for( int i = Whead[u] ; i ; i = w[i].pre ){
        int v = w[i].to ;
        if( dis[v] != dis[u] + 1 || !w[i].flow ) continue ;
        int nowf = dfs( v , min( flow , w[i].flow ) ) ;
        if( nowf ){
            totf += nowf ;
            flow -= nowf ;
            w[i].flow -= nowf ;
            w[i^1].flow += nowf ;
        }
    }
    if( !totf ) dis[u] = -1 ;
    return totf ;
}

void solve(){
    SPFA() ;
    build() ;
    int ans = 0 ;
    while( Bfs() )
        ans += dfs( 1 , 0x3f3f3f3f ) ;
    printf( "%d" , ans ) ;
}

inline int read_(){
    int rt = 0 ;
    char ch = getchar() ;
    while( ch < '0' || ch > '9' ) ch = getchar() ;
    while( ch >='0' && ch <='9' ) rt = (rt<<1) + (rt<<3) + ch - '0' , ch = getchar() ;
    return rt ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( register int i = 1 , u , v , t , c ; i <= M ; i ++ ){
        u = read_() , v = read_() , t = read_() , c = read_() ;
        In( u , v , t , c ) ;
        In( v , u , t , c ) ;
    }
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值