[关闭]
@himnouth 2022-08-22T14:42:25.000000Z 字数 3626 阅读 385

题解

Atcoder Beginner Contest 257 problem D 题解

本题解作者himnouth,原题目链接:https://atcoder.jp/contests/abc257/tasks/abc257_d

题目大意

张蹦床,每张蹦床i在的位置有一个属性值;一个人本身有一个属性值。定义两蹦床之间的距离为:
一个人在蹦床能够跳到蹦床,当且仅当满足:

样例输入1

  1. 4
  2. -10 0 1
  3. 0 0 5
  4. 10 0 1
  5. 11 0 1

样例输出1

  1. 2

样例解释1

时,不存在一张蹦床,使得从此开始可以跳到所有蹦床。
时,他可以从号蹦床开始,跳到所有蹦床。
例如,他跳到号蹦床可以用如下操作:

样例输入2

  1. 7
  2. 20 31 1
  3. 13 4 3
  4. -10 -15 2
  5. 34 26 5
  6. -2 39 4
  7. 0 -50 1
  8. 5 -20 2

样例输出2

  1. 18

数据范围

解析

这道题当然可以暴力解决。
从小到大枚举,对每一个,枚举每一个点,看从此点能否遍历到每一个点,时间复杂度。(巨佬randnameaaa亲测,还是能过几个点的)。
不过,这种暴力解法却给我们提供了一些思路。
求S的最小值,想到优化自然能想到二分。二分,若能从某一个点到达其他所有点,那么可能存在一个更优的使得条件成立。若不能,则需要一个更大S的来使得条件成立。

事实上,还有一种更高明的方法,如下:

不难发现,如果把所有点看做一个图上的点,那么从一个点到达其他所有点,对应的就是这个图是一个连通图。
在使用算法求解最短路时,常列出递推式,如下:

将其稍作变动为:

以此递推式,可求出在路径中每一次跳跃的距离最大值,再求出这些最大值的最小值即可。
此外,将一式稍作变形,可得:

由上,我们可在计算时,将其先除以。因为dis_{i,j}为整数,所以需将其向上取整。

代码实现

以上算法代码实现如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define N 205
  4. long long n,answer,dis[N][N],x[N],y[N],p[N],f[N][N];
  5. int main(){
  6. std::ios::sync_with_stdio(0);
  7. cin>>n;
  8. for(long long i=1;i<=n;i++) cin>>x[i]>>y[i]>>p[i];
  9. for(long long i=1;i<=n;i++){
  10. for(long long j=1;j<=n;j++){
  11. long long dis=abs(x[i]-x[j])+abs(y[i]-y[j]);
  12. if(dis%p[i]==0) f[i][j]=dis/p[i];
  13. else f[i][j]=dis/p[i]+1;
  14. }
  15. }
  16. for(long long k=1;k<=n;k++){
  17. for(long long i=1;i<=n;i++){
  18. for(long long j=1;j<=n;j++){
  19. f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));
  20. }
  21. }
  22. }
  23. long long ans=0xffffffff;
  24. for(long long i=1;i<=n;i++){
  25. long long res=-0x7fffffff;
  26. for(int j=1;j<=n;j++){
  27. res=max(res,f[i][j]);
  28. }
  29. ans=min(ans,res);
  30. }
  31. cout<<ans;
  32. return 0;
  33. }

另附使用二分算法代码如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=205;
  4. #define int long long //超级懒狗
  5. int n;
  6. int x[N],y[N],p[N];
  7. int a[N][N]; //a[i][j]表示从i到j的距离
  8. bool b[N][N]; //邻接矩阵
  9. inline bool check(int mid){
  10. memset(b,0,sizeof b); //多次check,需要初始化
  11. for(int i=1;i<=n;i++)
  12. for(int j=1;j<=n;j++)
  13. if(i!=j)
  14. b[i][j]=(p[i]*mid>=a[i][j]); //若i能到j,则b[i][j]=1;否则为0。
  15. for(int k=1;k<=n;k++)
  16. for(int i=1;i<=n;i++)
  17. for(int j=1;j<=n;j++)
  18. if(i!=j&&j!=k&&i!=k)
  19. b[i][j]|=b[i][k]&b[k][j]; //Floyd模板
  20. for(int i=1;i<=n;i++){
  21. bool g=1;
  22. for(int j=1;j<=n;j++)
  23. if(b[i][j]==0&&i!=j) g=0;
  24. if(g) return 1;
  25. } //枚举每个点,看从此点能否到达其他所有点,即是否b[i]中除第i个数以外的元素都为1
  26. return 0;
  27. }
  28. signed main() //signed超懒型
  29. {
  30. cin>>n;
  31. for(int i=1;i<=n;i++){
  32. scanf("%lld%lld%lld",&x[i],&y[i],&p[i]);
  33. for(int j=1;j<i;j++)
  34. a[i][j]=a[j][i]=abs(x[i]-x[j])+abs(y[i]-y[j]); //计算距离
  35. }
  36. int l=0,r=1e10,ans=0;
  37. while(l<=r){
  38. int mid=(l+r)/2;//long long间不能使用位运算
  39. if(check(mid)) r=mid-1,ans=mid;
  40. else l=mid+1;
  41. } //二分模板(注意l与r的取值)
  42. cout<<ans;
  43. return 0;
  44. }

又附使用暴力算法代码如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=205;
  4. int n;
  5. struct node{
  6. int id,dis;
  7. bool operator<(const node w)const{
  8. return dis<w.dis;
  9. }
  10. };
  11. vector<node>v[N];
  12. int x[N],y[N],p[N];
  13. bool vis[N];
  14. inline bool dfss(int dep,int k,int s){
  15. int ans=0;
  16. queue<int>q;
  17. q.push(s);
  18. while(1){
  19. if(ans==n) return 1;
  20. if(q.size()==0) return 0;
  21. int t=q.front();
  22. q.pop();ans++;
  23. int j=0;
  24. while(v[t][j].dis<=dep*p[t]&&j<v[t].size()){
  25. if(vis[v[t][j].id]) {j++;continue;}
  26. q.push(v[t][j].id);
  27. vis[v[t][j].id]=1;
  28. j++;
  29. }
  30. }
  31. return 0;
  32. }
  33. inline bool dfs(int dep,int k){
  34. for(int i=1;i<=n;i++){
  35. memset(vis,0,sizeof vis);
  36. vis[i]=1;
  37. if(dfss(dep,k,i)) return 1;
  38. }
  39. return 0;
  40. }
  41. int main()
  42. {
  43. cin>>n;
  44. for(int i=1;i<=n;i++)
  45. cin>>x[i]>>y[i]>>p[i];
  46. for(int i=1;i<=n;i++){
  47. for(int j=1;j<=n;j++)
  48. if(i!=j)
  49. v[i].push_back({j,abs(x[i]-x[j])+abs(y[i]-y[j])});
  50. sort(v[i].begin(),v[i].end());
  51. }
  52. for(int i=1;i<=n;i++){
  53. for(int j=0;j<v[i].size();j++)
  54. cout<<v[i][j].dis<<" ";
  55. cout<<endl;
  56. }
  57. int depth=0;
  58. while(!dfs(depth,1))
  59. depth++;
  60. cout<<depth;
  61. }

版权声明:

以上暴力算法代码及二分算法代码与题目大意皆来源于巨佬randnameaaa的题解,解析改编自巨佬randnameaaa的题解。未经过版主授权,不得随意转载。若有侵犯randnameaaa版权,请与作者联系。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注