• Educational Codeforces Round 133 (Rated for Div. 2)


    A. 2-3 Moves

    题目链接:Problem - A - Codeforces

    样例输入:

    1. 4
    2. 1
    3. 3
    4. 4
    5. 12

    样例输出:

    1. 2
    2. 1
    3. 2
    4. 4

    题意: 给定一个n,问我们至少要经过多少次操作可以从0号点到达n号点,每次操作我们可以选择向左或者向右移动2步或者3步。

    分析:由贪心策略知我们肯定是尽可能地选择每次移动3步的操作,如果一直向右移动3步最后剩余2步那么我们最后就直接移动2步,而如果最后剩余1步,那么按道理来说我们应该向右移动3步再向左移动2步,但是这样会多一次操作,不如我们上一次在离n点距离为4的时候直接选择两次向右移动2步的操作,这样会节省一些时间,还有一种特殊情况需要我们特判一下,就是n=1的情况,这种情况我们没办法回退一次,所以只能是先向右移动3步再向左移动2步。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. using namespace std;
    9. const int N=1e5+10;
    10. int main()
    11. {
    12. int T;
    13. cin>>T;
    14. while(T--)
    15. {
    16. int n;
    17. scanf("%d",&n);
    18. n=abs(n);
    19. if(n%3==0) printf("%d\n",n/3);
    20. else printf("%d\n",n/3+1);
    21. }
    22. return 0;
    23. }

    B. Permutation Chain

    题目链接:Problem - B - Codeforces

    样例输入: 

    1. 2
    2. 2
    3. 3

    样例输出:

    1. 2
    2. 1 2
    3. 2 1
    4. 3
    5. 1 2 3
    6. 3 2 1
    7. 3 1 2

    题意:每次给定一个n,给定一个1~n的排列数组a,f(a)等于\sum_{i=1}^{n}(i==a[i]),让我们构造一个最大的k,满足f(a1)>f(a2)>……>f(ak)且排列ai是由排列ai-1交换其中一对数对得到,输出a1~k。

    分析:其实通过分析不难得到我们可以构造一个k=n的情况,而且由于排列ai是由排列ai-1交换其中一对数对得到,那么第一次交换至少要使得f值减2,所以一定有k<=n,下面介绍一种k==n的构造方法。一开始我们的数组肯定是ai=i的,然后我们从i=2开始,每次令a[i]与a[1]交换,这样就能够使得f值减1,而且满足所给规则。

    1. #include<iostream>
    2. #include<algorithm>
    3. #include<cstring>
    4. #include<cstdio>
    5. #include<cmath>
    6. #include<vector>
    7. #include<queue>
    8. using namespace std;
    9. const int N=1e5+10;
    10. int a[N];
    11. int main()
    12. {
    13. int T;
    14. cin>>T;
    15. while(T--)
    16. {
    17. int n;
    18. scanf("%d",&n);
    19. for(int i=1;i<=n;i++) a[i]=i;
    20. printf("%d\n",n);
    21. for(int i=1;i<=n;i++) printf("%d ",a[i]);
    22. puts("");
    23. for(int i=2;i<=n;i++)
    24. {
    25. swap(a[1],a[i]);
    26. for(int j=1;j<=n;j++) printf("%d ",a[j]);
    27. puts("");
    28. }
    29. }
    30. return 0;
    31. }

    D. Chip Move

    题目链接:Problem - D - Codeforces

     样例输入:

    10 2
    

    样例输出:

    0 1 0 1 1 1 1 2 2 2 

    题意:给定一个n,我们要求出从0移动到x的方案数(1<=x<=n),给定一个k,移动规则是第一次移动的距离能够被k整除,第2次移动的距离能够被(k+1)整除,……,第m次移动的距离能够被(k+m-1)整除,求解到达1,2,……,n的不同方案数,注意每次移动距离都是大于0的。

    分析:我们先来看一下移动到x的最大操作次数是多少,不妨设k是1,那么就有1+2+3+……m>n,不难得到m是小于700的,也就是说操作轮次是不可能大于700的。

    f[i][j]表示j轮到达i点的方案数,然后容易想到f[i][j]=f[i-d][j]+f[i-d][j-1],d代表第j轮操作的最小距离,其中f[i-d][j]代表到达i-d这个点是通过移动d的倍数得到的,那么我们完全可以把倍数+1移动至i,而f[i-d][j-1]是通过前j-1轮操作到达的i-d,那么最少移动d到达i。

    这样我们就写完了状态转移方程,如下:

    1. for(int i=1;i<=n;i++)//枚举走到的位置
    2. {
    3. for(int j=1;j<=700&&i>=(k+j-1);j++)//枚举当前跳的轮次
    4. {
    5. f[i][j]=(f[i-(k+j-1)][j]+f[i-(k+j-1)][j-1])%mod;
    6. sum[i]=(sum[i]+f[i][j]);
    7. }
    8. printf("%d ",sum[i]);
    9. }

    但是这样显然有一个问题,就是空间复杂度有点高,也就是说700*n=1.4e8超过内存了,我们需要对其进行空间优化,通过观察状态转移方程我们不难发现,f[][j]只由f[][j]和f[][j-1]得到,于是根据以往空间优化的经验来说我们需要把内外层循环交换位置,并且使用滚动数组优化这个状态转移方程即可,代码如下:

    1. for(int j=1;j<=700;j++)//枚举当前跳的轮次
    2. {
    3. for(int i=1;i<=n;i++)//枚举走到的位置
    4. {
    5. if(1ll*(2*k+j-1)*j>1ll*2*i) continue;
    6. f[i][1]=(f[i-(k+j-1)][1]+f[i-(k+j-1)][0])%mod;
    7. sum[i]=(sum[i]+f[i][1])%mod;
    8. }
    9. for(int i=1;i<=n;i++)
    10. f[i][0]=f[i][1],f[i][1]=0;
    11. }

    其中我们用if语句进行了剪枝,原理就是j轮操作至少移动的位置是k+(k+1)+……+(k+j-1)=(2*k+j-1)*m/2,而这个位置是要小于等于i的,否则就不可能满足题意。空间优化的方法就是每次用f[][0]和f[][1]更新f[][1],最后再把值赋给f[][0],这个地方不要忘记把f[i][1]初始化为0,否则会出错。

  • 相关阅读:
    创建运行nnunet的docker镜像,并且使用nnunet训练自己的2D数据
    什么是PCB中的光学定位点,不加可不可以?
    SAS学习6(freq过程、tabulate过程、univariate过程、plot过程、chart过程)
    图的拓扑排序(入门篇)
    next项目部署到云服务器上(手动)
    单例设计模式c++
    Design a TinyURL
    Flume集成Kafka
    【21天算法挑战赛】排序算法——希尔排序
    Ae 效果:CC Bender
  • 原文地址:https://blog.csdn.net/AC__dream/article/details/126855223