题目地址: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; }
写于2020/7/31 14:23