1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——poj3662 Telephone Lines

藍書(演算法競賽進階指南)刷題記錄——poj3662 Telephone Lines

題目大意:找一條從1到n的路徑,使得這條路徑上第k+1大的邊最小.

解法一:

我們可以假裝這是一張有向無環圖DAG,那麼我們就可以設計一個DP陣列f[i][j]表示達到第i個點時免費維修了j條邊的最小代價.

那麼我們就可以發現方程即為:

f[e[i].y][j]=min(max(f[k][j],e[i].v),f[k][j-1]).

但是這不是一張有向無環圖,具有後效性,我們並不能這麼跑一個DP.

所以我們用最短路代替DP,解決後效性的問題.

於是我們可以使用SPFA來做這道題,時間複雜度\bg_white O(eNK),其中若不特殊構造資料,則e為一個較小的常數.

程式碼如下:

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
  using namespace std;
#define Abigail inline void
const int N=1000,M=10000;
const int INF=(1<<30)-1;
int n,m,k;
struct side{
  int y,next,v;
}e[M*2+9];
int lin[N+9],top;
struct state{
  int node,use;
};
queue<state>q;
int dis[N+9][M+9];
bool use[N+9][M+9];
void ins(int x,int y,int v){
  e[++top].y=y;e[top].v=v;
  e[top].next=lin[x];
  lin[x]=top;
}
state make_state(int x,int y){
  state t;
  t.node=x,t.use=y;
  return t;
}
void spfa(int sp,int sk){
  for (int i=1;i<=n;i++)
    for (int j=0;j<=k;j++)
      dis[i][j]=INF;
  q.push(make_state(sp,sk));
  dis[sp][sk]=0;
  use[sp][sk]=1;
  while (!q.empty()){
    state t=q.front();
    q.pop();use[t.node][t.use]=0;
    for (int i=lin[t.node];i;i=e[i].next){
      if (max(dis[t.node][t.use],e[i].v)<dis[e[i].y][t.use]){
        dis[e[i].y][t.use]=max(dis[t.node][t.use],e[i].v);
        if (!use[e[i].y][t.use]){      //這裡不能用continue代替,否則下面的語句就不會執行 
          use[e[i].y][t.use]=1;
          q.push(make_state(e[i].y,t.use));
        }
      }
      if (t.use<k&&dis[t.node][t.use]<dis[e[i].y][t.use+1]){
        dis[e[i].y][t.use+1]=dis[t.node][t.use];
        if (!use[e[i].y][t.use+1]){
          use[e[i].y][t.use+1]=1;
          q.push(make_state(e[i].y,t.use+1));
        }
      }
    }
  }
}
Abigail into(){
  scanf("%d%d%d",&n,&m,&k);
  int x,y,l;
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&l);
    ins(x,y,l);ins(y,x,l);
  }
}
Abigail work(){
  spfa(1,0);
}
Abigail outo(){
  if (dis[n][k]^INF) printf("%d\n",dis[n][k]);
  else printf("-1\n");
}
int main(){
  into();
  work();
  outo();
  return 0;
}

解法二:

我們可以很容易發現這道題中,若我們花費的錢更多時,那麼合法的方案已定越多,且已定包含花費更少的方案,所以我們發現這道題可以使用二分花費轉化為判定問題.

我們確定一個花費mid後,我們可以將大於這個花費的邊的邊權都設為1,小於的都設為0,那麼我們就可以跑一遍最短路.

由於這是0-1邊權最短路問題,所以我們可以使用一個雙端佇列BFS來進行求解.

程式碼如下:

//#include<bits/stdc++.h>
#include<iostream>
#include<deque>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=1000,M=10000;
const int INF=(1<<20)-1;
int n,m,k;
struct side{
  int y,next,v,use;
}e[M*2+9];
int top,lin[N+9];
int dis[N+9],use[N+9],ans;
deque<int>q;
void ins(int x,int y,int v){
  e[++top].y=y;e[top].v=v;
  e[top].next=lin[x];
  lin[x]=top;
}
bool check(int mid){
  for (int i=1;i<=top;i++)
    e[i].use=e[i].v>mid?1:0;
  for (int i=1;i<=n;i++)
    use[i]=0,dis[i]=INF;
  dis[1]=0;use[1]=1;
  q.push_back(1);
  while (!q.empty()){
    int t=q.front();
    q.pop_front();use[t]=2;
    for (int i=lin[t];i;i=e[i].next)
      if (dis[t]+e[i].use<dis[e[i].y]){
        dis[e[i].y]=dis[t]+e[i].use;
        if (use[e[i].y]) continue;
        use[e[i].y]=1;
        if (e[i].use) q.push_back(e[i].y);
        else q.push_front(e[i].y); 
      }
  }
  return dis[n]<=k;
}
Abigail into(){
  scanf("%d%d%d",&n,&m,&k);
  int x,y,v;
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&v);
    ins(x,y,v);ins(y,x,v);
  }
}
Abigail work(){
  ans=INF;
  for (int i=19;i>=0;i--)
    if (check(ans-(1<<i))) ans-=1<<i;
}
Abigail outo(){
  printf("%d\n",ans^INF?ans:-1);
}
int main(){
  into();
  work();
  outo();
  return 0;
}