1. 程式人生 > 實用技巧 >2020hdu多校第三場1009(6797)Tokitsukaze and Rescue

2020hdu多校第三場1009(6797)Tokitsukaze and Rescue

題目地址http://acm.hdu.edu.cn/showproblem.php?pid=6797

題意:有n個點的完全無向圖,也就是有n(n-1)/2條無向邊,刪除k條邊之後使得從一號節點到n號節點的最短路徑最長,求出次最短路徑的最大值

輸入:第一行一個整數t,表示樣例個數

每個樣例第一行兩個整數n,k

接下來n(n-1)/2行,每行三個整數u,v,w,表示u,v之間存在一條權值為w的無向邊

輸出:對於每個樣例輸出一個整數,表示刪除k條邊之後的最短路徑的最大值

題解:這裡面n<=50,k<=min(n-2,5),所以這裡直接dfs遞迴深搜就可以,但比賽的時候沒敢嘗試,的確非常後悔。先找出一條最短路徑,遍歷刪除路徑上的每一條邊,然後繼續dfs,在刪除求出的最短路徑上的每一條邊,如此直到刪除k條邊表示一次dfs結束,知道左右可能的情況都遍歷之後求出的就是最後的答案

AC程式碼

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring> 
using namespace std;
const int N=50+2;
int e[N][N];
int p[N];
int dis[N];
int n,k,sum;
int pre[6][N];
struct node{
    int x,s;
}a,b;
bool operator>(struct node a,struct node b){
    return a.s>b.s;
}
int dij(int x){ priority_queue<struct node,vector<struct node>,greater<struct node> >q; memset(p,0,sizeof(p)); memset(dis,0,sizeof(dis)); p[1]=1; a.x=1,a.s=0; q.push(a); while(!q.empty()){ a=q.top();q.pop(); p[a.x]=1; if(a.x==n) break
; for(int i=2;i<=n;i++){ if(p[i]||e[i][a.x]==-1) continue; b.x=i,b.s=a.s+e[a.x][i]; if(dis[i]==0||b.s<dis[i]){ q.push(b); dis[i]=b.s; pre[x][i]=a.x;//這裡表示刪除x條邊之後的最短路徑中i的前一個結點, //第一次寫的時候直接使用的一維陣列,結果一直WA,最後才發現原來當你下一次深搜之後pre就會被改變, //當返回上一層的dfs時繼續便利pre就不是原先的pre,就會產生錯誤 } } } while(!q.empty()) q.pop(); return a.s; } void dfs(int x){ if(x==k){ if(sum==-1) sum=dij(x); else sum=max(sum,dij(x)); return ; } dij(x);//當刪除x條邊之後進行的最短路搜尋 int now=n,w;//之前寫的是now=pre[x][now]然後一直出錯,原來要是這麼寫的話,那麼與n節點相連的邊就不會被遍歷到了 while(pre[x][now]!=-1){ w=e[now][pre[x][now]]; e[now][pre[x][now]]=-1; e[pre[x][now]][now]=-1;//之前沒有這一行與下面有註釋的那行,然後一直WA,最後才發現自己是真的馬虎 dfs(x+1); e[now][pre[x][now]]=w; e[pre[x][now]][now]=w;//與上面標註的那行同時沒有寫 now=pre[x][now]; } } int main(){ int t;cin>>t; while(t--){ sum=-1; memset(pre,-1,sizeof(pre)); cin>>n>>k; int u,v; for(int i=1;i<=n*(n-1)/2;i++){ cin>>u>>v; cin>>e[u][v];//使用它鄰接矩陣儲存圖 e[v][u]=e[u][v]; } dfs(0);//深搜,0表示當前已經刪除0條邊 cout<<sum<<endl; } return 0; }
View Code

寫於2020/7/31 14:23