1. 程式人生 > >分層圖最短路(題集)

分層圖最短路(題集)

最近遇到的這種題有點多。。。乾脆做一些題集吧。
(不定期更新)
題意: 給你 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;
}