分層圖最短路(題集)

最近遇到的這種題有點多。。。乾脆做一些題集吧。
(不定期更新)
題意: 給你 n 個點 , m 條邊 , k條免費路徑的權限
然後讓你求最短路
直接套SPFA ,然後加點修改
定義: 我們定義一個二維數組ddt[i][j] , 表示第 i 個點 , 免費了 j 條路 ;
故而, 我們的標記數組也不必多說 findv[i][j] 。
然後存好圖就直接跑就行了。
算法思想:
對於我們當前找到的終點,嘗試起點的狀態去更新,不選擇此條邊免費的狀態和選擇此條邊免費的狀態,再將這兩個狀態壓入隊列去更新可以到達的其他狀態。
代碼的題目爲2018年南京網絡賽裏面的一道圖論題
代碼一: (這個是一個學長寫的 = =)


#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include <cassert>
#include <time.h>
#include <iomanip>
using namespace std;
#define  PI acos(-2)
#define llf float
#define inf 0x7f7f7f7f
#define INF 0x3f3f3f3f3f3f3f3f
const int maxn = 1e5 + 10;
typedef long double lllld;
typedef long long ll;
typedef pair<int, int> ii;
typedef pair<double, double> ldd;
typedef string ss;
typedef vector<ss> vss;

int findv[11][maxn];
ll ddt[11][maxn];
int n, idx, k;

#define read(FILENAME) freopen((FILENAME + ".in").c_str(), "r", stdin)
#define write(FILENAME) freopen((FILENAME + ".out").c_str(), "w", stdout)



struct point {
    int to, next;
    ll val;
}E[maxn * 4];
int head_1[maxn];

struct Stu {
    int p;
    int u;
    ll d;
};

void init() {
    idx = 0;
    memset(head_1, -1, sizeof(head_1));
}

void solve();
void add(int x, int y, ll val);

int main() {
    int c, b, a;
    int i;
    int tt;
    scanf("%d", &tt);
    int x;
    while (tt--) {
        int m;
        scanf("%d%d%d", &n, &m, &k);
        init();
        for (i = 0; i < m; i++) {
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, (ll)c);
        }
        solve();
    }
    return 0;
}

void solve() {
    queue<Stu> QQ;
    memset(findv, 0, sizeof(findv));
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= k; j++)
            ddt[j][i] = INF;
    }
    for (int i = 0; i <= k; i++) ddt[i][1] = 0;
    QQ.push(Stu{0, 1, 0});
    findv[0][1] = 1;
    int i;
    int ss = 0;
    int sss = 100005;
    while (!QQ.empty()) {
        ss++;
        if (ss > sss) break;
        Stu now = QQ.front();
        QQ.pop();
        findv[now.p][now.u] = 0;
        for (i = head_1[now.u]; i != -1; i = E[i].next) {
            int to = E[i].to;
            if (ddt[now.p][to] > ddt[now.p][now.u] + E[i].val) {
                ddt[now.p][to] = ddt[now.p][now.u] + E[i].val;
                if (findv[now.p][to] == 0) {
                    QQ.push(Stu{now.p, to, ddt[now.p][to]});
                    findv[now.p][to] = 1;
                }
            }
            if (ddt[now.p + 1][to] > ddt[now.p][now.u]) {
                ddt[now.p + 1][to] = ddt[now.p][now.u];
                if (findv[now.p + 1][to] == 0 && now.p + 1 <= k) {
                    QQ.push(Stu{now.p + 1, to, ddt[now.p][to]});
                    findv[now.p + 1][to] = 1;
                }
            }
        }
    }
    long long flag = INF;
    for (i = 0; i <= k; i++) {
        flag = min(flag, ddt[i][n]);
    }
    printf("%lld\n", flag);
}

void add(int x, int y, ll val) {
    E[idx].to = y;
    E[idx].val = val;
    E[idx].next = head_1[x];
    head_1[x] = idx++;
}

代碼二: 一樣的方法,不過第一個是spfa , 第二個是dijstra
(這個是我看着人家的題解一個一個慢慢敲出來的 = =)

#include<bits/stdc++.h>
using namespace std ;
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
 const int maxn = 1e6 + 10 ;

int vis[maxn][20] ;
int n , m , k ;
LL dis[maxn][20] ;
int head[maxn] ;
struct node{
  int to , next ;
  LL val ;
  node(){} ;
     node(int to, int nx, LL w) : to(to), next(nx), val(w) {}
 }st[maxn ] ;

 struct NODE{
   int xp , to ;
   LL w ;
   NODE(){} ;
   NODE(int t , int p , LL val){ to = t ; xp = p ; w = val ;  }
     bool operator < (const NODE &r) const
    {
        return w > r.w;
    }
 };
 int cnt ;

 void add(int u, int v, LL w)
{
    st[++cnt] = node(v, head[u], w); head[u] = cnt;
}


 void dijstra(){
  for(int i = 1 ; i <= n ; i++) for(int j = 0 ; j <= k ; j++) dis[i][j] = INF , vis[i][j] = 0 ;

  priority_queue<NODE> q ; q.push(NODE(1 ,0 , 0 )) ; dis[1][0] = 0 ;

   while(!q.empty()){
      int u = q.top().to ;
      int p = q.top().xp ;
      LL cost = q.top().w ;
      q.pop() ;
    //  cout <<cost << endl ;
      if(vis[u][p] == 1) continue ;
      vis[u][p] = 1 ;
      dis[u][p] = cost ;
      for(int i = head[u] ; i != -1 ; i = st[i].next ){
           int v = st[i].to ;
           int c = st[i].val;
           if(dis[u][p] + c < dis[v][p] ) { dis[v][p] = dis[u][p] + c ; q.push(NODE(v, p ,dis[v][p] )) ; }
           if(p + 1 <= k && dis[u][p]< dis[v][p+1] ) { dis[v][p+1] = dis[u][p] ;  q.push(NODE(v  , p+1 , dis[v][p+1] ) ) ; }
      }
   }
 }

   void init(){
    memset(head , -1 , sizeof(head)) ;
    cnt = 0 ;

  }


int main(){
    int  t ;
    cin >> t ;
    while(t--){
             init() ;
        scanf("%d %d %d",&n,&m,&k) ;
         int u , v ;
         LL w ;
        for(int i = 0 ; i < m ; i++){
            scanf("%d%d%lld",&u,&v,&w) ;
            add(u , v , w ) ;
        }
        dijstra();
        LL ans = dis[n][k] ;
        printf("%lld\n",ans) ;
    }
  return 0 ;
}

題二 :
CCF無線網絡
題目:
問題描述

  目前在一個很大的平面房間裏有 n 個無線路由器,每個無線路由器都固定在某個點上。任何兩個無線路由器只要距離不超過 r 就能互相建立網絡連接。
  除此以外,另有 m 個可以擺放無線路由器的位置。你可以在這些位置中選擇至多 k 個增設新的路由器。
  你的目標是使得第 1 個路由器和第 2 個路由器之間的網絡連接經過儘量少的中轉路由器。請問在最優方案下中轉路由器的最少個數是多少?

輸入格式

  第一行包含四個正整數 n,m,k,r。(2 ≤ n ≤ 100,1 ≤ k ≤ m ≤ 100, 1 ≤ r ≤ 108)。
  接下來 n 行,每行包含兩個整數 xi 和 yi,表示一個已經放置好的無線 路由器在 (xi, yi) 點處。輸入數據保證第 1 和第 2 個路由器在僅有這 n 個路由器的情況下已經可以互相連接(經過一系列的中轉路由器)。
  接下來 m 行,每行包含兩個整數 xi 和 yi,表示 (xi, yi) 點處可以增設 一個路由器。
  輸入中所有的座標的絕對值不超過 108,保證輸入中的座標各不相同。

輸出格式

  輸出只有一個數,即在指定的位置中增設 k 個路由器後,從第 1 個路 由器到第 2 個路由器最少經過的中轉路由器的個數。

樣例輸入

5 3 1 3
0 0
5 5
0 3
0 5
3 5
3 3
4 4
3 0

樣例輸出

2
思路:
在這個題中,我們設立dis[n+m][k] 的二維數組表示 第N個點,使用了k個可選取的點, 然後跑SPFA,在其中加入隊列的操作中添加一些條件就好,求出第一個點到第二個點的距離。
然後我們遍歷下dis[1][i] 這個數組,然後取最小值即可 。
參考鏈接(CCF無線網絡)
代碼如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std ;
int head[10050] ;
int tot = 0 ;
int dis[10500][210] ;
int vis[10500][210] ;
 int n , m , k , r ;
struct Edge
{
    int to,next;
}edge[10050];
struct node{
  int u , v ;
};
void addedges(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    edge[tot].to = u;
    edge[tot].next = head[v];
    head[v] = tot++;
}
void init(){
  tot = 0 ;
  memset(head , -1 , sizeof(head)) ;
}

int x[1050] , y[1050] ;
void SPFA(){
  memset(vis , 0  , sizeof(vis ) ) ;
  memset(dis , 0x3f , sizeof(dis ) ) ;
  queue<node> q ;
  node a , now ;
   a.u = 0 , a.v = 0 ;
   q.push(a) ; dis[0][0] = 0 ; vis[0][0] = 1 ;
   while(!q.empty()){
    now = q.front() ; q.pop() ;
    int u = now.u ;
    int kk = now.v ;
    vis[u][kk] = 0 ;
    for(int i = head[u] ; i!= -1 ; i = edge[i].next ){
         int v = edge[i].to ;
         int kk = now.v ;
         if(v >= n) kk++ ;
         if(dis[u][now.v] + 1 < dis[v][kk] && kk <= k ) {
             dis[v][kk] = dis[u][now.v] + 1 ;
             a.v = kk ; a.u = v ;
             if(vis[v][kk] == 0 ) {
                vis[v][kk] = 1 ;
                q.push(a) ;
             }
         }
    }
   }
}
int p[1050][1050] ;
int main(){

      cin >> n >> m >> k >>r ;
         init();
    for(int i=0;i<n+m;i++) scanf("%d%d",&p[i][0],&p[i][1]);
    for(int i=0;i<n+m;i++)
    {
        for(int j=i+1;j<n+m;j++)
        {
            int x = p[i][0] - p[j][0];
            int y = p[i][1] - p[j][1];
            if( (long long int)x*x +(long long int) y*y <=(long long int) r*r ) addedges(i,j);
        }
    }
      SPFA( ) ;

      int ans = 999 ;
   /*   for(int i = 0 ; i <= n+m ; i++){
        for(int j = 0 ; j <= n+m ; j++)
        cout << dis[i][j] <<" " ;
        cout <<endl ;
      }
*/


    for(int i=0;i<=k;i++) ans = min(ans  ,dis[1][i]) ;
    printf("%d\n",ans - 1);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章