Codeforces Round #598 (Div. 3)

A Payment Without Change
B Minimize the Permutation
C Platforms Jumping
D Binary String Minimizing

A. Payment Without Change

题目链接:A题链接

题目大意:第一行给出q个查询。接下来2 - q+1行每行输入a 、b、n、s,要你判断是否存在n * x + y = s,其中x的范围 [ 0,a ], y 的范围 [0, b]

解题思路:先总结一下我两种错误的解题思路,(1)通过两层for循环对x和y进行枚举,但是由于数据较大,超时了
**在这稍微记录一下,109的(n2)会有很大几率超时 ,该题给出的时间限制是time limit per test1 second,一秒,学习了~**如果是给2000ms的话估计就很好过了
(2)第二个错误的思路

        /*
		 * 注意!该代码块是错误思路!
		 */
        int i = a;
        while(i*n > s){
            i--;
        }
        if(s - i*n <= b && s - i*n >= 0)
            printf("YES\n");
        else
            printf("NO\n");

我是想用最多的面值为n的纸币,如果连循环都没有进入的话,那么就直接所有的n面值的纸币都使用了,那么剩下的差值,如果能用b张一元纸币补齐的话说明题目的式子是可以成立的,然而代码wa在了第三个测试样例

样例是这样的

10000
158260522 877914575 602436426 24979445
861648772 623690081 433933447 476190629
731963982 822804784 450968417 430302156
982631932 161735902 880895728 923078537
404539679 303238506 317063340 492686568
462224593 492601449 384836991 191890310
576823355 782177068 404011431 818008580
954291757 160449218 155374934 840594328
164163676 797829355 138996221 501899080
353195922 545531545 910748511 350034067
131381221 846045895 208032501 346948152
190870607 189766466 554374432 137831502
810900084 888…

思考了一下错误原因,大概是i= a,i * n的时候爆int了,数据达到了9e16了,远超int的范围
( int的取值范围为: -231——231-1,即-2147483648——2147483647 1)
10^9的数据不要乱乘

正确的解题思路: 求出最多能用几张面值为n的纸币,那么这里有两种情况
1)n面值的纸币一共a张所有都用了,还是离s值有一定距离
2)不需要用a那么多张,而是需要用s/n张,再多用一张就超出s值了

因此得出 t = min(s/n, a);

/**
 *    This code has been written by YueGuang, feel free to ask me question. Blog: http://www.yx.telstudy.xyz
 *    created:
 */
#include <cstdio>
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define REP(i, a, b) for(int i = a; i < b; i++)
#define REP_(i, a, b) for(int i = a; i <= b; i++)
#define sl(n) scanf("%lld", &n);
#define si(n) scanf("%d", &n);
#define RepAll(a) for(auto x: a)
#define cout(ans) cout << ans << endl;
typedef long long ll;

using namespace std;
int main(){
    //freopen("in.txt", "r", stdin);
    int q; scanf("%d", &q);
    while(q--){
        int a, b, n, s; scanf("%d%d%d%d", &a, &b, &n, &s);
        int t = min(s/n, a); // 能出多少张n面值的纸币
        if (s - t*n <= b) printf("YES\n");
        else printf("NO\n");
    }
}

B. Minimize the Permutation

题目链接:B题链接

题目类型:贪心 模拟

题目大意:给你一段序列,对序列进行n-1次操作,每次只能做如下操作,i位置上的数和i - 1位置上的数字互换(i >= 2 i <= n)(一共n个数字),最后使得该序列字典序最小

注意题目中的这句话

The second line of the test case contains n distinct integers from 1
to n — the given permutation.

大意是n个数字都是不同的,所以1-n个数字都是会出现的,那么他们的数值会和他们的位置有很大的关系,就比如如果2是在2的位置上那么很明显,是不用再进行交换操作了

样例解释:
4
5
5 4 1 3 2

查找到的最小数,<-> 表示两数位置交换

  1. 1 1<-> 4 交换完的序列 5 1 4 3 2
  2. 1 1<-> 5 交换完的序列 1 5 4 3 2
  3. 2 2<-> 3 交换完的序列 1 5 4 2 3
  4. 2 2<-> 4 交换完的序列 1 5 2 4 3

由于已经操作n-1次了,所以最终得到的最小序列就是1 5 2 4 3

解题思路:根据上述样例解释的过程,我们很清楚的明白只需要在序列中查找最小的那个值向前移动就可以了,只需要增加一个判断数组,如果 i 位置上的数字大于i-1位置上的数,就直接置false,下次查找最小值的时候不找他了

错误代码

/**
 *    This code has been written by YueGuang, feel free to ask me question. Blog: http://www.yx.telstudy.xyz
 *    created:
 */
#include <cstdio>
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define REP(i, a, b) for(int i = a; i < b; i++)
#define REP_(i, a, b) for(int i = a; i <= b; i++)
#define sl(n) scanf("%lld", &n);
#define si(n) scanf("%d", &n);
#define RepAll(a) for(auto x: a)
#define cout(ans) cout << ans << endl;
typedef long long ll;

using namespace std;
const int maxn = 110;
int a[maxn];
bool tpl[maxn * 10];
int main(){
    freopen("in.txt", "r", stdin);
    int q; scanf("%d", &q);
    while(q--){
        printf("%d\n", q);
        memset(tpl, true, sizeof(tpl)); //tpl用于判断这个位置是否需要继续进行操作
        int n; scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        tpl[1] = false;

        int ftime = 0;
        while(ftime < n-1){
            int pos = 1, mi = a[1];
            for(int i = 1; i <= n; i++){
                if(a[i] < mi && tpl[i]){
                    mi  = a[i];
                    pos = i;
                }
            }
            if(tpl[pos] == false) break;
            if(a[pos - 1] > a[pos]){
                int tmp    = a[pos - 1];
                a[pos - 1] = a[pos];
                a[pos]     = tmp;
                ftime++;
            }
            if(a[pos - 1] <= a[pos]){
                tpl[pos] = false;
            }
            if(tpl[n] == false){break;}
        }
        for(int i = 1; i <= n; i++) i == 1 ? printf("%d", a[i]) : printf(" %d", a[i]);
        printf("\n");
    }
}

错误原因,没有注意到题目所提及的1-n都是不同的数字,因此把模拟做的复杂,甚至还跑不出来或者跑错

正确代码

/**
 *    This code has been written by YueGuang, feel free to ask me question. Blog: http://www.yx.telstudy.xyz
 *    created:
 */
#include <cstdio>
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define REP(i, a, b) for(int i = a; i < b; i++)
#define REP_(i, a, b) for(int i = a; i <= b; i++)
#define sl(n) scanf("%lld", &n);
#define si(n) scanf("%d", &n);
#define RepAll(a) for(auto x: a)
#define cout(ans) cout << ans << endl;
typedef long long ll;

using namespace std;
const int maxn = 110;
int a[maxn];
bool tpl[maxn * 10];
int main(){
    //freopen("in.txt", "r", stdin);
    int q; scanf("%d", &q);
    while(q--){
        //printf("%d\n", q);
        //memset(tpl, false, sizeof(tpl)); //tpl用于判断这个位置是否需要继续进行操作
        int n; scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]),tpl[i] = false;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if (a[j] == i){
                    for(int k = j - 1; k >= 1; k--){
                        if(a[k] > a[k+1] && !tpl[k] ){ swap(a[k], a[k + 1]), tpl[k] = true;}
                    }
                }
            }


        }
        for(int i = 1; i <= n; i++) i == 1?printf("%d", a[i]):printf(" %d", a[i]);
        puts("");
    }
}

  1. 第一层for循环遍历序列的所有位置
  2. 第二层for循环遍历序列的所有值
  3. 第三层for循环是从j - 1开始向前移动,先不移动位置值和数值相同的,去前面找后值小于前值的位置,进行交换,每交换一次,k位置锁死,tpl[ k ] = false;

C Platforms Jumping

题目链接:C题链接

题目类型:模拟,贪心

题目大意:一条河长度为 n。你从 0 的位置出发,要走到n+1位置才算度过这条河。 题目为你提供了m块长度分别为 a[i]的木板,帮助你度过小河,你每次可以跳跃的最长距离为 d,最短距离为 1。现在要你回答是否能渡过小河,如果不行输出NO否则输出Yes。

Yes的情况下,第二行输出的是什么呢?
输出的是 如果该位置有木板则输出木板的索引,比如这个位置用的木板是a[i],所以这个位置输出的值就是i,如果是跳过的地方,也就是没有木板的地方,你输出的就是0

解题思路:个人觉得该题的细节很多,有这么几个需要思考的地方

(1)人出发的位置是0,最后到达的位置是n+1,不是n这个对于判断是否能过河至关重要
(2)人每次走的距离是 a[i]+d-1,因为最后到达的是木板的位置,不是木板后一位
(3)数组要够大

解题的方法主要是在贪心的思路上进行模拟,每次跳最多的格子数,然后放板子

/**
 *    This code has been written by YueGuang, feel free to ask me question. Blog: http://www.yx.telstudy.xyz
 *    created:
 */
#include <cstdio>
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define REP(i, a, b) for(int i = a; i < b; i++)
#define REP_(i, a, b) for(int i = a; i <= b; i++)
#define sl(n) scanf("%lld", &n);
#define si(n) scanf("%d", &n);
#define RepAll(a) for(auto x: a)
#define cout(ans) cout << ans << endl;
typedef long long ll;

using namespace std;
const int maxn = 2e5+10;
int a[maxn], ans[maxn];
int main(){
    int n, m, d, sum = 0; scanf("%d%d%d", &n, &m, &d);
    for(int i = 1; i <= m; i++) scanf("%d", &a[i]), sum += d + a[i] - 1;
    sum+=d;
    if (sum >= n+1){
         printf("YES\n");
        sum = 0; int pos = 0;//当前坐标
        //从pos位置开始移动
        REP(i, 1, m+1){sum += a[i];}
        for(int i = 1; i <= m; i++){
            if (pos + sum + d -1 <= n) pos += d;
            else pos = n - sum + 1; //到终点的距离,不能再跳了,剩下都铺木板

            //除了pos到pos+a[i]-1的位置是索引下标,其他地方都是0,也就是没有木板的地方
            for(int j = pos; j <= pos + a[i] - 1; j++){
                ans[j] = i;
            }

            pos += a[i]-1;//在木板上跳的距离
            sum -= a[i]; //位置发生改变,为pos + sum + d -1 <= n判断做准备
        }
        for(int i = 1; i <= n; i++){
            cout << ans[i] << " ";
        }

    }
    else{
        printf("NO\n");

    }
    return 0;
}

posted @ 2019-11-05 16:09  月光不染是非  阅读(123)  评论(0编辑  收藏  举报