洛谷P1401 城市

题目描述

N(2<=n<=200)个城市,M(1<=m<=40000)条无向边,你要找T(1<=T<=200)条从城市1到城市N的路,使得最长的边的长度最小,边不能重复用。

输入输出格式

输入格式:

第1行三个整数N,M,T用空格隔开。

第2行到P+1行,每行包括三个整数Ai,Bi,Li表示城市Ai到城市Bi之间有一条长度为Li的道路。

输出格式:

输出只有一行,包含一个整数,即经过的这些道路中最长的路的最小长度。

输入输出样例

输入样例#1: 
7 9 2
1 2 2
2 3 5
3 7 5
1 4 1
4 3 1
4 5 7
5 7 1
1 6 3
6 7 3
输出样例#1: 
5








二分答案+网络流。。。
一开始的思路是 spfa 记录最短路径,一条一条删边。
但是样例把 T 改为3 就炸了。。。
又看到 最长路最小——这TM不是二分答案么???

于是 二分答案+dinic。。。

把用过的边打上标记,没用过的边若流量小于二分的mid,设为0,否则设为1。

注:双向边。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define MAXN 210
#define MAXM 40010
#define MAX 999999999
using namespace std;
int n,m,k,s,t,c=2;
int head[MAXN],deep[MAXN];
struct node{
    int next,to,w;
    bool vis;
}a[MAXM<<2],b[MAXM<<2];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline void add(int u,int v,int w){
    a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
    a[c].to=u;a[c].w=0;a[c].next=head[v];head[v]=c++;
}
void reset(int x){
    for(int i=2;i<c;i++){
        if(a[i].w==0||a[i].w<=x||a[i].w==MAX)a[i].vis=true;
        else a[i].vis=false;
        b[i]=a[i];
        if(a[i].w!=0&&a[i].w!=MAX)b[i].w=1;
    }
}
bool bfs(){
    int u,v;
    queue<int> q;
    for(int i=0;i<=n+2;i++)deep[i]=0;
    deep[s]=1;
    q.push(s);
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=head[u];i;i=b[i].next){
            v=b[i].to;
            if(b[i].vis&&b[i].w&&!deep[v]){
                deep[v]=deep[u]+1;
                if(v==t)return true;
                q.push(v);
            }
        }
    }
    return false;
}
int dfs(int x,int limit){
    if(x==t)return limit;
    int v,cost=0,sum;
    for(int i=head[x];i;i=b[i].next){
        v=b[i].to;
        if(b[i].vis&&b[i].w&&deep[v]==deep[x]+1){
            sum=dfs(v,min(limit-cost,b[i].w));
            if(sum>0){
                b[i].w-=sum;
                b[i^1].w+=sum;
                cost+=sum;
                if(cost==limit)break;
            }
            else deep[v]=-1;
        }
    }
    return cost;
}
bool check(int x){
    int ans=0;
    reset(x);
    while(bfs())ans+=dfs(s,MAX);
    if(ans>=k)return true;
    return false;
}
void init(){
    int u,v,w,l,r=0,mid,ans=0;
    n=read();m=read();k=read();
    s=n+1;t=n+2;
    for(int i=1;i<=m;i++){
        u=read();v=read();w=read();
        r=max(r,w);
        add(u,v,w);add(v,u,w);
    }
    add(s,1,MAX);add(n,t,MAX);
    while(l<=r){
        mid=l+r>>1;
        if(check(mid)){
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    printf("%d\n",ans);
}
int main(){
    init();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值