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