nuoyanli 520 Let‘s play computer game
H题
描述
xxxxxxxxx在疫情期间迷上了一款游戏,这个游戏一共有nnn个地点(编号为1——n1——n1——n),他每次从一个地点移动到另外一个地点需要消耗
一定的能量,每一个地点都有一些珠宝,输入中会把每一个地方的珠宝价值估算成一个值。
xxxxxxxxx想请聪明的你帮他找出来从编号为sss的地点,到编号为ddd的地点最小要消耗多少能量,消耗这些能量最多获得多少价值的珠宝
输入
第一行输入n,s,dn,s,dn,s,d。其意义如上描述 (n<520n<520n<520)
第二行有nnn个数XiX_iXi,比如i==1i==1i==1描述编号为1的点有珠宝价值为XiX_iXi(这里保证XiX_iXi大于等于0) (Xi<1000X_i<1000Xi<1000)
下面输入一个n∗nn*nn∗n的矩阵,比如第iii行,第j列的值为xxx,就表示从编号为iii到编号为jjj的地方距离为x (x<1000)x (x<1000)x (x<1000)
注意:这是一个单向路,即从编号为iii到编号为jjj的距离可能不等于从编号为jjj到编号为iii
输出
输出占一行,第一个数是最小要消耗多少能量,第二个数是消耗这些能量最多获得多少价值的珠宝。两个数用空格分开
输入样例 1
4 1 4
20 30 40 10
0 1 2 3
999 0 999 2
999 999 0 2
999 999 999 0
输出样例 1
3 60
输入样例 2
7 2 3
341 527 189 740 490 388 989
0 489 711 174 305 844 971
492 0 998 954 832 442 424
619 906 0 154 293 395 439
735 738 915 0 453 748 786
550 871 932 693 0 326 53
904 732 835 354 364 0 691
669 157 719 282 875 573 0
输出样例 2
998 716
提示
第一个样例解释:
解释:
把这个矩阵翻译成边
1->2 1
2->4 2
1->4 3
1->3 2
3->4 2
正确路径:1->2->4
题目正解:最短路+dfs 或 最短路+dp。
题目灵感:L2-001 紧急救援 (25分)。
题目大意描述:
让你找出来最短路径,如果最短路径有多条,那就取出来每一条路径中 所有点权值之和 最大的那个路径(点权值是什么意思?就是题目中说的每一个点的珠宝价值)
题目解法1:最短路+dfs
按照题目描述我们就需要先找出来最短路的大小,然后我们知道这个大小之后就dfs去跑图,找到每一条能从起点s到终点d的路径,然后每一条路径肯定有一个路径长度,我们只保留那个与最短路大小相等的长度的路径
然后对每一条路径在dfs过程中记录它所经过每一个点,把这些点的权值加到一起,我们设这个值为总值
那么每一条路径都有一个总值,我们只需要从中找出来最大的那个输出就行
Dfs代码如下:
void dfs(int x,int value) { if(x==d) { if(result<value) { result=value; } return; } for(int i=1; i<=n; ++i) { if(!vis[i] && dist[i]==dist[x]+G[x][i] && G[x][i]) { vis[i]=1; dfs(i,value+val[i]); vis[i]=0; } } }
这里我们为了避免盲目的搜索所有路径,我们可以加一个限制条件来优化dfs:
dist[i]==dist[x]+G[x][i] //dist是我们跑最短路之后跑出来的单源最短路数组
假设一条路径例如s->2->3->d,如果你dfs过程中s->2->3的长度不等于dist[3] ,那么就算dfs到终点d这条路径长度绝对不可能等于dist[d]
至于为什么这里就不多说了,自己想一下
代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<vector> 7 using namespace std; 8 typedef long long ll; 9 const int inf = 0x3f3f3f3f; 10 const int maxn = 550; 11 int n,m,s,d; 12 int val[maxn],vis[maxn],dist[maxn]; 13 int G[maxn][maxn],result; 14 int int_rand(int min,int max) 15 { 16 return int(min+rand()%(max-min)); 17 } 18 struct shudui1 19 { 20 int start,value; 21 bool operator < (const shudui1 q)const 22 { 23 return value<q.value; 24 } 25 } str1; 26 priority_queue<shudui1>r; 27 void dijkstra() 28 { 29 memset(vis,0,sizeof(vis)); 30 memset(dist,inf,sizeof(dist)); 31 dist[s]=0; 32 str1.start=s; 33 str1.value=0; 34 r.push(str1); 35 while(!r.empty()) 36 { 37 int x,y; 38 str1=r.top(); 39 r.pop(); 40 x=str1.start; 41 y=str1.value; 42 if(dist[x]<y) continue; 43 for(int i=1; i<=n; ++i) 44 { 45 if((dist[x]+G[x][i]<dist[i])) 46 { 47 dist[i]=dist[x]+G[x][i]; 48 str1.start=i; 49 str1.value=dist[i]; 50 r.push(str1); 51 } 52 } 53 } 54 } 55 void dfs(int x,int value) 56 { 57 if(x==d) 58 { 59 60 if(result<value) 61 { 62 result=value; 63 } 64 return; 65 } 66 for(int i=1; i<=n; ++i) 67 { 68 if(!vis[i] && dist[i]==dist[x]+G[x][i] && G[x][i]) 69 { 70 vis[i]=1; 71 72 dfs(i,value+val[i]); 73 74 vis[i]=0; 75 } 76 } 77 } 78 int main() 79 { 80 int u,v,w; 81 scanf("%d%d%d", &n, &s, &d); 82 for(int i = 1; i <= n; i ++) 83 { 84 scanf("%d", &val[i]); 85 } 86 for(int i = 1; i <= n; i ++) 87 { 88 for(int j = 1; j <= n; j ++) 89 { 90 scanf("%d",&G[i][j]); 91 } 92 } 93 dijkstra(); 94 result=0; 95 memset(vis,0,sizeof(vis)); 96 vis[s]=1; 97 dfs(s,val[s]); 98 printf("%d %d\n",dist[d],result); 99 return 0; 100 }
题目解法2:最短路+dp
题外话:这个方法其实我并没有想到,还是yzj验题之后给me说的(牛掰)
for(ll i=0; i<len; i++) { ll v=vec[t][i].v; ll w=vec[t][i].w; if(dis[t]+w<dis[v]) { dp[v]=dp[t]+sc[v]; //当s->v的最短路径已然变得更小,那么dp值要重新赋值,而不是比较之后再赋值 dis[v]=dis[t]+w; if(!vis[v]) { vis[v]=1; q.push(v); } } else if(dis[t]+w==dis[v]) //相对于原版spay算法,这里多了一个判断。多这个判断的意义和上面第一种解法的dfs里面多一个判断那个差不多 { if(dp[v]<dp[t]+sc[v])//这里要想给dp[v]赋值就要通过判断,因为我们要输出总值最大的那个路径 { dp[v]=dp[t]+sc[v]; if(!vis[v]) { vis[v]=1; q.push(v);//如果这个点满足条件,那么我们还要把这个点放入队列,那么相比于原spay算法这个代码复杂度就要大一点,但是还是要比第一种方法复杂度低 } } } }
Dp关键部分就是上面代码,也就是在spay算法的while循环里面
解释一下每一个数组:
Dis数组是存放单源最短路结果
Vis数组是用来标记某一个点是否进入队列中(等于1就代表在队列)
Dp[i]数组初值是等于i点的权值(点权值是什么意思?就是题目中说的每一个点的珠宝价值),这个数组的作用就是找出来题目第二个输出结果
Sc[]数组初值是等于i点的权值
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const ll maxn=2e3+5; 5 ll sc[maxn],dis[maxn],vis[maxn],ans; 6 ll dp[maxn],pre[maxn]; 7 struct node 8 { 9 ll v,w; 10 } p; 11 vector<node>vec[maxn]; 12 vector<ll>ve[maxn]; 13 queue<ll>q; 14 void spfa(ll u) 15 { 16 memset(dis,0x3f3f3f3f,sizeof(dis)); 17 memset(vis,0,sizeof(vis)); 18 dis[u]=0; 19 q.push(u); 20 while(!q.empty()) 21 { 22 ll t=q.front(); 23 vis[t]=0; 24 q.pop(); 25 ll len=vec[t].size(); 26 for(ll i=0; i<len; i++) 27 { 28 ll v=vec[t][i].v; 29 ll w=vec[t][i].w; 30 if(dis[t]+w<dis[v]) 31 { 32 dp[v]=dp[t]+sc[v]; 33 dis[v]=dis[t]+w; 34 if(!vis[v]) 35 { 36 vis[v]=1; 37 q.push(v); 38 } 39 } 40 else if(dis[t]+w==dis[v]) 41 { 42 if(dp[v]<dp[t]+sc[v]) 43 { 44 dp[v]=dp[t]+sc[v]; 45 if(!vis[v]) 46 { 47 vis[v]=1; 48 q.push(v); 49 } 50 } 51 } 52 } 53 } 54 } 55 int main() 56 { 57 ios::sync_with_stdio(false); 58 cin.tie(0); 59 ll n,s,d; 60 cin>>n>>s>>d; 61 for(ll i=1; i<=n; i++) 62 cin>>sc[i],dp[i]=sc[i]; 63 ll w; 64 for(ll i=1; i<=n; i++) 65 for(ll j=1; j<=n; j++) 66 { 67 if(i==j) 68 cin>>w; 69 else 70 cin>>w,p.v=j,p.w=w,vec[i].push_back(p); 71 } 72 spfa(s); 73 cout<<dis[d]<<' '<<dp[d]<<endl; 74 }