分层图最短路(题集)

最近遇到的这种题有点多。。。干脆做一些题集吧。
(不定期更新)
题意: 给你 n 个点 , m 条边 , k条免费路径的权限
然后让你求最短路
直接套SPFA ,然后加点修改
定义: 我们定义一个二维数组ddt[i][j] , 表示第 i 个点 , 免费了 j 条路 ;
故而, 我们的标记数组也不必多说 findv[i][j] 。
然后存好图就直接跑就行了。
算法思想:
对于我们当前找到的终点,尝试起点的状态去更新,不选择此条边免费的状态和选择此条边免费的状态,再将这两个状态压入队列去更新可以到达的其他状态。
代码的题目为2018年南京网络赛里面的一道图论题
代码一: (这个是一个学长写的 = =)


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include 
#include 
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 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 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
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 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 <
      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
#include
#include
#include
#include
#include
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 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;iscanf("%d%d",&p[i][0],&p[i][1]);
    for(int i=0;ifor(int j=i+1;jint 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 <


    for(int i=0;i<=k;i++) ans = min(ans  ,dis[1][i]) ;
    printf("%d\n",ans - 1);
    return 0;
}

你可能感兴趣的:(ACM图论,最短路,分层图)