题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6797
题目大意:n个点的完全图,要求删k条边,求删完k条边后的最短路的最大值。
3≤n≤50,1≤k≤min(n−2,5) 边权为[ 1 , 10^4 ] 的随机数
题解:
删的边一定在当前的最短路上,跑一遍dijstra,暴力枚举删最短路上的哪条边,然后进行下一轮删边,变成删除k-1条边的子问题。
在删完k条边时,跑一遍用当前的dis[n]更新ans。
ps:1. 记录边的时候开一个pre记录每个点前面的结点,然后能顺着找到路径。
2.因为有好几轮记录,但是是按深度dfs的,最多有k层,要把pre开成二维数组,最多 pre[k][n]就可以了。 【一开始没想到是按深度的,把每轮dijstra的pre都记录了,pre开的非常大,有pre[1000000][50],也没炸】
正确性:
因为题目中一句重要的:
在边权随机的情况下,最短路的边条数很小。【官方的吐槽:一条路A 一条路B A的边数比B多,要使A的长度<B的长度,在每条边值域相同的情况下概率很小的】
时间复杂度 (n^2 * e^k)e是最短路的边数。
当时没想到,要不早就爆搜了!!qwq
官方标程【觉得写得非常的简洁,先放出来】:
1.pre不用开大
2.稠密图直接写n^2的dijstra,不用堆优化
1 #include<cstdio> 2 const int N=55,K=15,inf=100000000; 3 int Case,n,m,i,j,x,y,z,g[N][N],d[N],f[K][N],v[N],ans; 4 void dfs(int m){ 5 int i,j,k; 6 for(i=1;i<=n;i++)d[i]=inf,f[m][i]=v[i]=0; 7 for(d[1]=0,i=1;i<=n;i++){ 8 k=0; 9 for(j=1;j<=n;j++)if(!v[j])if(!k||d[j]<d[k])k=j; 10 v[k]=1; 11 for(j=1;j<=n;j++)if(!v[j]&&d[k]+g[k][j]<d[j]){ 12 d[j]=d[k]+g[k][j]; 13 f[m][j]=k; 14 } 15 } 16 if(!m){ 17 if(ans<d[n])ans=d[n]; 18 return; 19 } 20 for(i=n;i!=1;i=j){ 21 j=f[m][i]; 22 k=g[i][j]; 23 g[i][j]=g[j][i]=inf; 24 dfs(m-1); 25 g[i][j]=g[j][i]=k; 26 } 27 } 28 int main(){ 29 scanf("%d",&Case); 30 while(Case--){ 31 scanf("%d%d",&n,&m); 32 for(i=1;i<=n;i++)for(j=1;j<=n;j++)g[i][j]=i==j?0:inf; 33 for(i=1;i<=n*(n-1)/2;i++){ 34 scanf("%d%d%d",&x,&y,&z); 35 g[x][y]=g[y][x]=z; 36 } 37 ans=0; 38 dfs(m); 39 printf("%d ",ans); 40 } 41 }
2.个人ac代码
弊端:
1.pre数组开的太大
2.堆优化dijstra比较冗余
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int INF = 0x3f3f3f3f; 5 const int MAXN = 55,mu=1000000; 6 const int MAXM = 3025; 7 int pre[mu][MAXN],mp[MAXN][MAXN],t[MAXM],d[MAXN],tot; 8 9 inline int read() { 10 int x = 0, ff = 1; char ch = getchar(); 11 while(!isdigit(ch)) { 12 if(ch == '-') ff = -1; 13 ch = getchar(); 14 } 15 while(isdigit(ch)) { 16 x = (x << 1) + (x << 3) + (ch ^ 48); 17 ch = getchar(); 18 } 19 return x * ff; 20 } 21 int n, m, k; 22 struct love 23 { 24 int num, d; 25 }; 26 27 bool operator < (love a, love b) 28 { 29 return a.d > b.d; 30 } 31 /*used 是否使用过结点 32 d 到结点1的距离 33 */ 34 priority_queue <love> q; 35 bool used[MAXN]; 36 void dijkstra() 37 { 38 memset(used,0,sizeof(used)); 39 for(int i=0;i<=n;i++)d[i]=INF; 40 int s=1; 41 q.push((love){s, 0}); 42 d[s] = 0; 43 used[s] = 1; 44 while (!q.empty()) 45 { 46 int now = q.top().num; 47 q.pop(); 48 used[now] = 1; 49 for (int i = 1; i <=n; i ++) 50 { 51 if (d[i] > d[now] + mp[now][i]) 52 { 53 d[i] = d[now] + mp[now][i]; 54 pre[tot][i]=now; 55 if (!used[i]) 56 { 57 q.push((love){i, d[i]}); 58 } 59 } 60 } 61 } 62 } 63 int totans; 64 65 void dfs(int cnt){ 66 tot++; 67 int num=tot; 68 dijkstra(); 69 if(cnt==k+1){ 70 totans=max(totans,d[n]); 71 return; 72 } 73 int uu=n; 74 while(uu!=1){ 75 //暴力枚举删最短路上的哪条边 76 // printf("uu: %d pre[uu]: %d ",uu,pre[uu]); 77 int gg=pre[num][uu]; 78 int tmp=mp[uu][gg]; 79 mp[uu][gg]=INF; 80 mp[gg][uu]=INF; 81 dfs(cnt+1);//搜完之后要回溯 82 mp[uu][gg]=tmp; 83 mp[gg][uu]=tmp; 84 uu=gg; 85 //cout<<"haha"<<uu<<endl; 86 } 87 } 88 int main(){ 89 int tt; 90 scanf("%d",&tt); 91 while(tt--){ 92 // cout<<INF<<endl; 93 n=read(); 94 k=read(); 95 m=(n-1)*n/2; 96 totans=0; 97 for(int i = 1; i <= m; ++i) { 98 int x,y,v; 99 x = read(); y = read(); v = read(); 100 mp[x][y]=mp[y][x]=v; 101 } 102 dfs(1); 103 printf("%d ",totans); 104 } 105 return 0; 106 } 107 /* 108 10 109 5 10 5 110 1 2 2990 111 1 3 2414 112 1 4 4018 113 1 5 6216 114 2 3 9140 115 2 4 4169 116 2 5 550 117 3 4 6618 118 3 5 3206 119 4 5 105 120 121 */