蓝书(算法竞赛进阶指南)刷题记录——poj3662 Telephone Lines

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82960879

题目:poj3662.

题目大意:找一条从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来进行求解.

双端队列BFS的时间复杂度为O(N+M),二分的时间复杂度为\bg_white O(log(maxL_i)),所以总的时间复杂度即为O(log(maxL_i)(N+M)).

代码如下:

//#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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82960879