• Codeforces Round #812 (Div. 2)补题(A-E)


    A. Traveling Salesman Problem

    题目链接:Problem - A - Codeforces

     题意:有一个xy坐标系,在x轴和y轴的上有若干个箱子,人物可以上下左右移动,问最少多少步能拿完所有箱子。

    思路:想象最短路径由一个长方形将四个角内凹,但这个图形的周长和原本长方形相同,所以只需找到一个最小的长方形把所有箱子罩住就行。寻找最左,最右,最上,最下的箱子,作为长方形的边界求边长

    代码实现:

    1. #include
    2. #define ll long long
    3. using namespace std;
    4. int main()
    5. {
    6. ios::sync_with_stdio(false);
    7. cin.tie(0);
    8. int t;
    9. cin>>t;
    10. while(t--)
    11. {
    12. int n;
    13. cin>>n;
    14. int l=0,r=0,top=0,b=0;//左右上下四个边界
    15. for(int i=1;i<=n;i++)
    16. {
    17. int x,y;
    18. cin>>x>>y;
    19. if(x==0)//寻找上下
    20. {
    21. top=max(top,y);
    22. b=min(b,y);
    23. }
    24. else//寻找左右
    25. {
    26. l=min(l,x);
    27. r=max(r,x);
    28. }
    29. }
    30. cout<<(r-l+top-b)*2<<'\n';
    31. }
    32. return 0;
    33. }

    B. Optimal Reduction

    题目链接:Problem - B - Codeforces

     题意:可以进行任意次让一个区间所有值减1的操作,给一个数组a,问a的元素全部变为0所需要的操作数是否小于等于所有“a中元素的任意排列”中元素全为0所需要的操作数。

    思路:

    考虑一个区间,不断进行操作,直到出现为0的数字,当0在这个区间两边时,操作的范围缩短即可,也就是不再对0进行操作,但当这个0在区间的中间时,会将这个区间变成两个区间,操作数增加,因此一个排列操作数最小的情况必须是对于1-n不能出现中间小两边大的情况,a必须满足最小的情况,对于每个下标,寻找其前面到下标1的最大值和后面到下标n的最大值,判断该元素是否比这两边最大值都小。

    代码实现:

    1. #include
    2. #define ll long long
    3. using namespace std;
    4. const int maxn=1e5+10;
    5. int a[maxn];
    6. int pre[maxn];
    7. int last[maxn];
    8. int main()
    9. {
    10. ios::sync_with_stdio(false);
    11. cin.tie(0);
    12. int t;
    13. cin>>t;
    14. while(t--)
    15. {
    16. int n;
    17. cin>>n;
    18. bool f=1;
    19. for(int i=1;i<=n;i++)
    20. {
    21. cin>>a[i];
    22. pre[i]=last[i]=a[i];
    23. }
    24. pre[n+1]=last[n+1]=0;
    25. for(int i=1;i<=n;i++)//预处理前缀最大值
    26. pre[i]=max(pre[i],pre[i-1]);
    27. for(int i=n;i>=1;i--)//预处理前缀最小值
    28. last[i]=max(last[i],last[i+1]);
    29. for(int i=1;i<=n;i++)
    30. if(a[i]-1]&&a[i]1])//a不是最小排列的情况
    31. f=0;
    32. if(f)
    33. cout<<"YES\n";
    34. else
    35. cout<<"NO\n";
    36. }
    37. return 0;
    38. }

    C. Build Permutation

    题目链接:Problem - C - Codeforces

     题意:

    给一个0-n-1的自然数,把这些数字放到下标为0-n-1的数组中,要求所有数组元素加上下标是一个数字的平方。输出一种放法。

    思路:首先,某个数的平方肯定不超过n-1+n-1,因为这是最大数了,设某个数x*x=i,那么(x+1)*(x+1)<=i*2,证:

    (x+1)*(x+1)=x*x+2*x+1

    因为都是整数,所以x*x+1<=i

    2*i-(x+1)*(x+1)>=2*x*x+2-x*x-2*x-1=(x-1)(x-1)>=0

    先枚举0-2n-2中所有的平方数,假设最初0-n-1所有元素都对应放在数组下标0-n-1,从n-1位置开始向前遍历,从最大的可能的平方数寻找,根据上面的结论,n-1到2*n-2之间肯定存在一个可能的数的平方,找到其中的最大的,假设n-1需要再加x等于这个平方数,且这个x肯定小于等于n-1,那么就拿n-1和下标为x(因为此时它的元素是x)交换,这样n-1就完成了,在考虑n-2,n-2只需要放x+2就可以得到和n-1位置一样的平方数,因此这个区间[x,n-1]原本的元素可以完全翻转,且不会影响到区间外前面的元素的选取。

    结论:实际上需要从n-1开始向前遍历,每个位置选取数字去匹配最大的可能平方数即可。

     代码实现:

    1. #include
    2. #define ll long long
    3. using namespace std;
    4. const int maxn=1e5+10;
    5. int ans[maxn];//记录某个元素被哪个位置选走,同样也记录某个位置选了哪个元素,因为都是0-n-1
    6. int main()
    7. {
    8. ios::sync_with_stdio(false);
    9. cin.tie(0);
    10. int t;
    11. cin>>t;
    12. while(t--)
    13. {
    14. memset(ans,-1,sizeof(ans));//初始还没选择答案
    15. int n;
    16. cin>>n;
    17. int ret=n;
    18. int sum=0;
    19. while(n*2-2>sum*sum)sum++;//记录最大可能的平方数+1
    20. for(int i=n-1;i>=0;i--)//
    21. {
    22. for(int j=sum;j>=0;j--)//枚举平方数
    23. {
    24. int x=j*j-i;//该位置所需的数字
    25. if(x>=0&&x-1)//这个数字在0-n-1范围内且还没被选走
    26. {
    27. ans[x]=i;//这个x被i选走
    28. break;
    29. }
    30. }
    31. }
    32. for(int i=0;i
    33. if(i==0)
    34. cout<
    35. else
    36. cout<<' '<
    37. cout<<'\n';
    38. }
    39. return 0;
    40. }

    D. Tournament Countdown

    交互题:通过cout之类的输出向系统问问题,系统会根据询问通过cin之类的输入告诉你答案,根据答案求出最终结果 

    题目链接:Problem - D - Codeforces

     题意:交互题,有2^n个选手打晋级赛(就那种16强进8强,8强进四强),给最多2*n/3次询问选手间胜负数量关系的机会,假设a与b,a的胜场比b多,回答1,b的胜场比a多,回答2,同样多,回答0,问谁是冠军。

    思路:当比赛处于同一层时,对于相邻两组选手(打完之后胜者互打),a对b,c对d。只需要从两组中各取一名选手,比如a,c,如果a比c多,那冠军不可能是c,并且也不可能是b,因此b淘汰,反过来c多,a,d淘汰,同样多时,因为a和b,c和d打完之后胜者肯定会打起来,因此ac肯定在此前被淘汰了。淘汰两人后,去找询问没被淘汰的两人,少的一方淘汰,多的进入下一轮。

    因此可以存储每一轮的选手,不断淘汰进入下一轮,直到比赛只存在一名选手或者两名选手,再进入下一轮。

    代码实现:

    1. #include
    2. #define ll long long
    3. using namespace std;
    4. const int maxn=1e5+10;
    5. int ans[maxn];
    6. int k[5];//记录两组选手
    7. int ask()
    8. {
    9. int x;
    10. cout<<'?'<<' '<1]<<' '<3]<//先询问选手1和选手2
    11. cin>>x;
    12. int a=0,b=0;//记录没被淘汰的两人
    13. switch(x)
    14. {
    15. case 0:a=k[2];b=k[4];break;
    16. case 1:a=k[1];b=k[4];break;
    17. case 2:a=k[3];b=k[2];break;
    18. }
    19. cout<<'?'<<' '<' '<
    20. cin>>x;
    21. if(x==1)
    22. return a;//返回两组最后胜出者
    23. return b;
    24. }
    25. int main()
    26. {
    27. int t;
    28. cin>>t;
    29. while(t--)
    30. {
    31. vector<int>a;
    32. int n;
    33. cin>>n;
    34. for(int i=1;i<=(1<
    35. a.push_back(i);
    36. while(a.size()>2)
    37. {
    38. vector<int>temp;
    39. for(int i=0;isize();i+=4)
    40. {
    41. for(int j=0;j<4;j++)//记录两组选手
    42. k[j+1]=a[i+j];
    43. temp.push_back(ask());//胜者进入下一轮
    44. }
    45. a=temp;
    46. }
    47. if(a.size()==2)//剩余两人,进行决斗
    48. {
    49. int x;
    50. cout<<'?'<<' '<0]<<' '<1]<
    51. cin>>x;
    52. if(x==1)
    53. cout<<'!'<<' '<0]<
    54. else
    55. cout<<'!'<<' '<1]<
    56. }
    57. else//只剩一人就直接输出
    58. cout<<'!'<<' '<0]<
    59. }
    60. return 0;
    61. }

    E. Cross Swapping

    题目链接:Problem - E - Codeforces

    题意:给一个n*n的矩阵,上面有元素aij,可以进行操作,给一个k,使得第k行和第k列互换,进行任意次操作后,输出字典序最小的矩阵,字典序小是从第一行第一个从左到右从上到下一行一行比较。 

    思路:想要交换a[i][j]和a[i][j],可以使i行i列交换或者j行j列交换,但是不能同时交换,因为会换回来,因此两者只能选其一,当不想交换a[i][j]和a[j][i]时,两种操作都要或者都不要,因此可以用并查集的思想,当i和j不能同时操作时,它们不在同一个集合并且要记录对立关系,将i+n和j合并,j+n和i合并,i+n代表和i对立的集合,j同样和i对立,因此就会加入i+n的集合,这样就能维护对立关系,当要维护i和j同时操作时,将i和j合并,i+n和j+n合并,因为i和i+n对立,j和j+n对立,i和j相同,那么j+n和i+n就相同,当两个元素对同一个元素对立,那么这个两个元素都要操作或者都不操作。如果a[i][j]和a[j][i]相同,随意,ij没有关系,越上面越前面的元素肯定是优先交换的, 因此发现i和j之前有对立关系,即使遍历到这需要让两个集合相同操作,也不能合并,因为优先满足前面的条件。

    代码实现:

    1. #include
    2. #define ll long long
    3. using namespace std;
    4. const int maxn=2e3+10;
    5. int fa[maxn];
    6. int a[maxn][maxn];
    7. int find(int x)//并查集
    8. {
    9. return x==fa[x]?x:fa[x]=find(fa[x]);
    10. }
    11. inline void merge(int x,int y)
    12. {
    13. x=find(x);
    14. y=find(y);
    15. if(x!=y)
    16. fa[x]=y;
    17. }
    18. int ans[maxn]; //记录是否操作
    19. int main()
    20. {
    21. ios::sync_with_stdio(false);
    22. cin.tie(0);
    23. int t;
    24. cin>>t;
    25. while(t--)
    26. {
    27. int n;
    28. cin>>n;
    29. for(int i=1;i<=2*n;i++)//初始化 1-n记录同类关系,n+1-2n记录对立关系
    30. {
    31. fa[i]=i;
    32. ans[i]=0;
    33. }
    34. for(int i=1;i<=n;i++)
    35. for(int j=1;j<=n;j++)
    36. cin>>a[i][j];
    37. for(int i=1;i<=n;i++)
    38. {
    39. for(int j=i;j<=n;j++)
    40. if(a[i][j]//不需要交换
    41. {
    42. if(find(i)==find(j+n)||find(i+n)==find(j))//先前存在i j所属集合对立关系
    43. continue;
    44. merge(i,j);
    45. merge(i+n,j+n);
    46. }
    47. else if(a[i][j]>a[j][i])//需要交换
    48. {
    49. if(find(i)==find(j)||find(i+n)==find(j+n))//先前存在 i j所属集合同类关系
    50. continue;
    51. merge(i,j+n);
    52. merge(i+n,j);
    53. }
    54. }
    55. for(int i=1;i<=n;i++)//让每个元素所属集合的对立集合和本集合不一样
    56. ans[find(i+n)]=ans[find(i)]^1;
    57. for(int i=1;i<=n;i++)//交换操作
    58. if(ans[find(i)])
    59. {
    60. for(int j=1;j<=n;j++)
    61. swap(a[i][j],a[j][i]);
    62. }
    63. for(int i=1;i<=n;i++)
    64. {
    65. for(int j=1;j<=n;j++)
    66. if(j==1)
    67. cout<
    68. else
    69. cout<<' '<
    70. cout<<'\n';
    71. }
    72. }
    73. return 0;
    74. }

  • 相关阅读:
    python开发-Flask与Vue基础
    AVL树左旋转,右旋转代码实现
    2022新版图文详解IDEA整合SSM框架(附源码)
    丐版电子沙漏
    OpenGL中最简单的窗体创建和渲染(初始化GLFW、GLAD、定义视口大小和resize回调、双层缓冲、输入事件处理)
    shell脚本安装apache服务
    1019 General Palindromic Number
    MapReduce 排序三种实现方式
    使用Terraform管理已经存在的kubernates和默认的节点池
    SingletonKit单例源码阅读学习
  • 原文地址:https://blog.csdn.net/m0_63737271/article/details/126217456