POJ3662 SPFA//二分 + 双端队列最短路

https://cn.vjudge.net/problem/12427/origin

题意:求1到N第K + 1大条边权最小的路径

 

首先想到dp递推,dp[x][y]表示到x这个点经过y条免费边的最小值。

直接借助SPFA递推即可

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 1010;
const int maxm = 20010;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
int N,M,tmp,K; 
struct Edge{
    int v,w,next;
}edge[maxm * 2];
int head[maxn],tot;
void init(){
    Mem(head,-1);
    tot = 0;
}
void add(int u,int v,int w){
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
bool vis[maxn][maxn];
int dp[maxn][maxn]; // 到达i经过p条免费边最大值
struct Node{
    int p,x,cost;
    Node(int x = 0,int p = 0,int cost = 0):x(x),p(p),cost(cost) {}
};
void SPFA(){
    queue<Node>Q;
    Q.push(Node(1,0,0));
    while(!Q.empty()){
        Node u = Q.front(); Q.pop();
        vis[u.x][u.p] = 0;
        for(int i = head[u.x];~i;i = edge[i].next){
            int v = edge[i].v;
            if(dp[v][u.p] > max(dp[u.x][u.p],edge[i].w)){
                dp[v][u.p] = max(dp[u.x][u.p],edge[i].w);
                if(!vis[v][u.p]){
                    vis[v][u.p] = 1;
                    Q.push(Node(v,u.p,dp[v][u.p]));
                }
            }
            if(u.p < K && dp[v][u.p + 1] > dp[u.x][u.p]){
                dp[v][u.p + 1] = dp[u.x][u.p];
                if(!vis[v][u.p + 1]){
                    vis[v][u.p + 1] = 1;
                    Q.push(Node(v,u.p + 1,dp[v][u.p + 1]));
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&N,&M,&K);
    init();
    For(i,1,M){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w); add(v,u,w);
    }
    memset(dp,0x3f,sizeof(dp));
    dp[1][0] = 0;
    SPFA();
    int ans = INF;
    _For(i,K,0){
        ans = min(ans,dp[N][i]);
    }
    if(ans == INF) Pri(-1);
    else Pri(ans);
    #ifdef VSCode
    system("pause");
    #endif
    return 0;
}
SPFA

第二个思想是二分答案,将大于这个答案的边权变为0,小于这个答案的边权变为1,直接跑1到N的最短路进行check

对于01图的最短路有一个技巧是用双端队列,类似BFS的方法跑,将权值为1的边进入的点加到队尾,权值0到达的点加到队尾即可。

#include <map>
#include <set>
#include <deque>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 1010;
const int maxm = 20010;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
int N,M,tmp,K; 
struct Edge{
    int v,w,next;
}edge[maxm * 2];
int head[maxn],tot;
void init(){
    Mem(head,-1);
    tot = 0;
}
void add(int u,int v,int w){
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
bool vis[maxn];
int dis[maxn];
bool check(int x){
    deque<int>Q;
    Q.push_back(1);
    Mem(vis,0);
    Mem(dis,0x3f);
    dis[1] = 0;vis[1] = 1;
    while(!Q.empty()){
        int u = Q.front(); Q.pop_front();
        if(u == N) return dis[u] <= K;
        for(int i = head[u]; ~i; i = edge[i].next){
            int v = edge[i].v; int w = edge[i].w;
            if(w <= x){
                dis[v] = min(dis[v],dis[u]);
                if(!vis[v]) Q.push_front(v);
            } 
            else{
                dis[v] = min(dis[v],1 + dis[u]);
                if(!vis[v]) Q.push_back(v);
            }
            vis[v] = 1; 
        }
    }
    return false;
}
int solve(int r){
    int l = 0;
    int ans = -1;
    while(l <= r){
        int m = (l + r) >> 1;
        if(check(m)){
            ans = m;
            r = m - 1;
        }else{
            l = m + 1;
        }
    }
    return ans;
}
int main()
{
    int MAX = 0;
    scanf("%d%d%d",&N,&M,&K);
    init();
    For(i,1,M){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        MAX = max(MAX,w);
        add(u,v,w); add(v,u,w);
    }
    Pri(solve(MAX));
    #ifdef VSCode
    system("pause");
    #endif
    return 0;
}

 

posted @ 2018-09-19 21:41  Hugh_Locke  阅读(390)  评论(0编辑  收藏  举报