CSP-J 2019 入门组/普及组

T1 数字游戏

题目点击→洛谷 P5660 数字游戏

题目描述
小 K 同学向小 P 同学发送了一个长度为 8 8 801 字符串 来玩数字游戏,小 P 同学想要知道字符串中究竟有多少个 1 1 1

注意:01 字符串为每一个字符是 0 0 0 或者 1 1 1 的字符串,如“101”(不含双引号)为一个长度为 3 3 3 的 01 字符串。

输入格式

输入只有一行,一个长度为 8 8 8 的 01 字符串 s s s

输出格式

输出只有一行,包含一个整数,即 01 字符串中字符 1 1 1 的个数。

数据范围

对于 20 20 20% 的数据,保证输入的字符全部为 0 0 0

对于 100 100 100% 的数据,输入只可能包含字符 0 0 0 和字符 1 1 1,字符串长度固定为 8 8 8

T1分析

送分题, 20 % 20\% 20% 数据直接输出 0 0 0 就可以,读取一个字符串,遍历一遍字符串,计数有多少个 1 1 1
当然这里有个小公式 : a + b + c = a b c ( m o d   9 ) a + b + c = abc ( mod\ 9) a+b+c=abc(mod 9)
所以这里可以直接输出 n n % 9 n

#include<bits/stdc++.h>
using namespace std; 
int main(){
    int n;
    cin >> n;
    cout << n % 9 << endl;
    return 0;
}

T2 公交换乘

题目点击→洛谷 P5661 公交换乘

题目描述
著名旅游城市 B 市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交车的优惠方案:

  1. 在搭乘一次地铁后可以获得一张优惠票,有效期为 45 45 45 分钟,在有效期内可以消耗这张优惠票,免费搭乘一次票价不超过地铁票价的公交车。在有效期内指开始乘公交车的时间与开始乘地铁的时间之差小于等于 45 45 45 分钟,即 t b u s − t s u b w a y ≤ 45 t_{bus}-t_{subway} \le 45 tbustsubway45

  2. 搭乘地铁获得的优惠票可以累积,即可以连续搭乘若干次地铁后再连续使用优惠票搭乘公交车。

  3. 搭乘公交车时,如果可以使用优惠票一定会使用优惠票;如果有多张优惠票满足条件,则优先消耗获得最早的优惠票。

现在你得到了小轩最近的公共交通出行记录,你能帮他算算他的花费吗?

输入格式

输入的第一行包含一个正整数 n n n,代表乘车记录的数量。

接下来的 n n n 行,每行包含 3 3 3 个整数,相邻两数之间以一个空格分隔。第 i i i 行的第 1 1 1 个整数代表第 i i i 条记录乘坐的交通工具, 0 0 0 代表地铁, 1 1 1 代表公交车;第 2 2 2 个整数代表第 i i i 条记录乘车的票价 p r i c e i price_i pricei;第三个整数代表第 i i i 条记录开始乘车的时间 t i t_i ti(距 0 0 0 时刻的分钟数)。

我们保证出行记录是按照开始乘车的时间顺序给出的,且不会有两次乘车记录出现在同一分钟。

输出格式

输出有一行,包含一个正整数,代表小轩出行的总花费。

数据范围

对于 30 30 30% 的数据, n ≤ 1000 n \le 1000 n1000 t i ≤ 1 0 6 t_i \le 10^6 ti106

另有 15 15 15% 的数据, t i ≤ 1 0 7 t_i \le 10^7 ti107 p r i c e i price_i pricei 都相等。

另有 15 15 15% 的数据, t i ≤ 1 0 9 t_i \le 10^9 ti109 p r i c e i price_i pricei 都相等。

对于 100 100 100% 的数据, n ≤ 1 0 5 n \le 10^5 n105 t i ≤ 1 0 9 t_i \le 10^9 ti109 1 ≤ p r i c e i ≤ 1000 1 \le price_i \le 1000 1pricei1000

T2分析

第二题,一个模拟题,不过这道题的模拟比往年的都要难一些,当然也没有特别难
读题需要仔细读,不要读错了
接下来就是如何做的问题了,这道题其实非常简单,对于每次乘车记录,如果是乘地铁,那就获得一张赠票,如果是公交,那就在所有可以用的赠票里找一张离现在最远的用掉
那么如果暴力去找赠票的话复杂度是 O ( n 2 ) O(n^2) O(n2) 显然会超时
所以这里需要一些优化
思考一下,坐车的记录是按顺序来的,那也就是说手里拿到赠票的时间是按时间排好的,那随着时间的推移,显然能用的赠票必然是赠票序列中的连续的一段
所以这就很像我们学习过的一种数据结构——队列
赠票全部从队尾加入队列,队头弹出所有过期的赠票,每次在队列中找一下所有可用的赠票,因为这个队列的长度不可能超过 45 45 45 ,所以不会超时
这里因为直接使用 S T L STL STL 的队列我们并不能非常方便的处理票用没用过的问题,所以我们还是用数组来模拟

#include <iostream>

using namespace std;
const int MAXN = 100005;
struct Ticket {
    //赠票的价格,最晚使用时间和是否需用过
    int price, time, used;
} q[MAXN];//赠票盒子
int head, tail, n, cost;

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        int op, price, time;
        //输入每次坐车的种类,价格和发车时间
        cin >> op >> price >> time;
        if (op == 0) {
            cost += price;
            q[tail].time = time + 45;
            q[tail++].price = price;
        } else {
            while (head < tail && q[head].time < time) {
                head++;
            }
            bool found = false;//表示是否有合适的赠票,先假设没有
            for (int j = head; j < tail; ++j) {
                if (q[j].price >= price && q[j].used == 0) {
                    //如果价格合适,并且没用过,标记找到了,这张票标记用过
                    found = true;
                    q[j].used = 1;
                    break;
                }
            }
            //如果没找到合适的赠票,老老实实花钱买吧
            if (!found) cost += price;
        }
    }
    cout << cost << endl;
    return 0;
}

T3 纪念品

题目点击→ 洛谷 P5662 纪念品

题目描述
小伟突然获得一种超能力,他知道未来 T T T N N N 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。

每天,小伟可以进行以下两种交易 无限次:

  1. 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;

  2. 卖出持有的任意一个纪念品,以当日价格换回金币。

每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。

T T T 天之后,小伟的超能力消失。因此他一定会在第 T T T 天卖出所有纪念品换回金币。

小伟现在有 M M M 枚金币,他想要在超能力消失后拥有尽可能多的金币。

输入格式

第一行包含三个正整数 T , N , M T,N,M T,N,M,相邻两数之间以一个空格分开,分别代表未来天数 T T T,纪念品数量 N N N,小伟现在拥有的金币数量 M M M

接下来 T T T 行,每行包含 N N N 个正整数,相邻两数之间以一个空格分隔。第 i i i 行的 N N N 个正整数分别为 P i , 1 , P i , 2 , … … , P i , N P_{i,1}, P_{i,2}, \ldots \ldots ,P_{i,N} Pi,1,Pi,2,,Pi,N,其中 P i , j P_{i,j} Pi,j 表示第 i i i 天第 j j j 种纪念品的价格。

输出格式

输出仅一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。

数据规模与约定

对于 10 10 10% 的数据, T = 1 T = 1 T=1

对于 30 30 30% 的数据, T ≤ 4 T \leq 4 T4 N ≤ 4 N \leq 4 N4 M ≤ 100 M \leq 100 M100,所有价格 $10 \leq P_{i,j} \leq 100 $。

对于 15 15 15% 的数据, T ≤ 100 T \leq 100 T100 N = 1 N = 1 N=1

对于 15 15 15% 的数据, T = 2 T = 2 T=2,$N \leq 100 $。

对于 100 100 100% 的数据, T ≤ 100 T \leq 100 T100 N ≤ 100 N \leq 100 N100 M ≤ 1 0 3 M \leq 10^3 M103,所有价格 $1 \leq P_{i,j} \leq 10^4 $,数据保证任意时刻,小明手上的金币数不可能超过 1 0 4 10^4 104

T3分析

首先前 10 % 10\% 10% 的分数是送的…只有一天所以输出 M M M 就可以了,再怎么样白送的分数要拿到…
然后这道题难点在于如何考虑买物品卖物品的问题
其实题目里多次强调,一件物品可以随时卖出,随时买进,买进卖出没有任何限制

所以这道题的关键点就在这里——我们没有必要考虑买进物品后,这个物品我们要拿几天卖掉,完全可以所有物品的操作都是今天买进明天卖出,我们每天先把手里有的物品全部卖光,再考虑今天要买什么
也就是说持有一件物品三天可以看成——第一天买进,第二天卖出后再买进,第三天卖出后再买进

只要考虑到上面这一点后,我们的问题就会被简化为,今天我们有 M M M 元钱,知道每个物品今天的价格和明天的价格,物品数量无限,问今天买进明天卖出后最多能获得多少钱

那么其实这个问题就是一个非常经典的问题了——完全背包

我们把每个物品的花费看做今天物品买入的价格,每个物品的价值就是这件物品 明 天 的 价 格 − 今 天 的 价 格 明天的价格-今天的价格 ,也就是说对第 n o w now now 天,第 i i i 件物品,构造 p r i c e [ i ] = p [ n o w ] [ i ] ; v a l u e [ i ] = p [ n o w ] [ i + 1 ] − p [ n o w ] [ i ] price[i] = p[now][i]; value[i] = p[now][i+1] - p[now][i] price[i]=p[now][i];value[i]=p[now][i+1]p[now][i] 物品数量无限,我们总共有 M M M 元钱,要获得最高的价值,那不就是一个完全背包吗?

所以这道题题目就完成了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
const int M = 1e4 + 5;
int t, n, m;
int a[N][N], f[M];

int main() {
	scanf("%d%d%d", &t, &n, &m);
    for (int i = 1; i <= t; ++i){
        for (int j = 1; j <= n; ++j){
    		scanf("%d", &a[i][j]);        
        }
    }
    for (int i = 1; i <= t; ++i) {
        memset(f, 0, sizeof f);
        for (int j = 1; j <= n; ++j){
            for (int k = a[i][j]; k <= m; ++k){
                f[k] = max(f[k], f[k - a[i][j]] + a[i+1][j] - a[i][j]);
            }
        }
        m = max(m, f[m] + m);
    }
    printf("%d\n", m);
    return 0;
}

T4 加工零件

点击查看→ 洛谷 P5663 加工零件

题目描述
凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 n n n 位工人,工人们从 1 1 1 ~ n n n 编号,某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。

如果 x x x 号工人想生产一个被加工到第 L ( L > 1 ) L(L > 1) L(L>1) 阶段的零件,则所有 x x x 号工人有传送带直接相连的工人,都需要生产一个被加工到第 L − 1 L-1 L1 阶段的零件(但 x x x 号工人自己无需生产第 L − 1 L-1 L1 阶段的零件)。

如果 x x x 号工人想生产一个被加工到第 1 1 1 阶段的零件,则所有 x x x 号工人有传送带直接相连的工人,都需要为 x x x 号工人提供一个原材料。

轩轩是 1 1 1 号工人。现在给出 q q q 张工单,第 i i i 张工单表示编号为 a i a_i ai 的工人想生产一个第 L i L_i Li 阶段的零件。轩轩想知道对于每张工单,他是否需要给别人提供原材料。他知道聪明的你一定可以帮他计算出来!

输入格式

第一行三个正整数 n , m n,m n,m q q q,分别表示工人的数目、传送带的数目和工单的数目。

接下来 m m m 行,每行两个正整数 u u u v v v,表示编号为 u u u v v v 的工人之间存在一条零件传送带。保证 u ≠ v u \neq v u=v

接下来 q q q 行,每行两个正整数 a a a L L L,表示编号为 a a a 的工人想生产一个第 L L L 阶段的零件。

输出格式

q q q 行,每行一个字符串“Yes”或者“No”。如果按照第 i i i 张工单生产,需要编号为 1 1 1 的轩轩提供原材料,则在第 i i i 行输出“Yes”;否则在第 i i i 行输出“No”。注意输出含引号。

数据范围

共 20 个测试点。

1 ≤ u , v , a ≤ n 1 \le u,v,a \le n 1u,v,an

测试点 1 1 1 ~ 4 4 4 1 ≤ n , m ≤ 1000 1 \le n,m \le 1000 1n,m1000 q = 3 q=3 q=3 L = 1 L=1 L=1

测试点 5 5 5 ~ 8 8 8 1 ≤ n , m ≤ 1000 1 \le n,m \le 1000 1n,m1000 q = 3 q=3 q=3 1 ≤ L ≤ 10 1 \le L \le 10 1L10

测试点 9 9 9 ~ 12 12 12 1 ≤ n , m , L ≤ 1000 1 \le n,m,L \le 1000 1n,m,L1000 1 ≤ q ≤ 100 1 \le q \le 100 1q100

测试点 13 13 13 ~ 16 16 16 1 ≤ n , m , L ≤ 1000 1 \le n,m,L \le 1000 1n,m,L1000 1 ≤ q ≤ 1 0 5 1 \le q \le 10^5 1q105

测试点 17 17 17 ~ 20 20 20 1 ≤ n , m , q ≤ 1 0 5 1 \le n,m,q \le 10^5 1n,m,q105 1 ≤ L ≤ 1 0 9 1 \le L \le 10^9 1L109

T4分析

这道题目的题意比较复杂,大概的意思就是说
如果有两个人 1 − 2 1-2 12 相连,那么此时如果 2 2 2 需要生产 3 3 3 号零件,那么 1 1 1 需要向 2 2 2 提供 2 2 2 号零件,而此时意味着 1 1 1 要生产 2 2 2 号零件,那就需要 2 2 2 1 1 1 提供 1 1 1 号零件…
整体的提供如下:其中 0 0 0 表示原材料
在这里插入图片描述
其实这个提供材料的过程是一个递归的过程

那么这道题的难点也就在这里了,我们需要多画图来观察这个递归关系,画图以后可以发现

2 2 2 号工人想生产偶数阶段的零件时,轩轩需要提供奇数阶段的零件
而如果 1 1 1 号工人轩轩需要偶数阶段的零件,那么 2 2 2 号工人需要提供奇数阶段的零件

把这个结论拓展开来可以发现

假设 i i i 号工人需要生产 L ( L > 0 ) L(L>0) L(L>0) 阶段的零件

那么只要 1 → i 1 \rightarrow i 1i之间存在一条长度 ≤ L \leq L L 并且和 L L L 的奇偶性相同的路径,
1 1 1 号工人就必须生产原材料

只要确定了这件事情,之后的问题就是:我们需要求出 1 1 1 到所有点的距离中,奇数长度的最短路和偶数长度的最短路

之所以求最短路,其实也很好理解,因为我们要保证 路 径 长 度 ≤ L 路径长度\leq L L 那当然求最小的了

那么现在的问题就是如何求最短路,个人认为这些数据点的区分就区分在了如何求最短路上…

那么这里有一个非常有趣而且简洁的做法——拆点

我们可以把一个点拆成奇偶两个点,也就是把 u − v u-v uv 拆分成 u 奇 − v 偶 u_奇-v_偶 uv u 偶 − v 奇 u_偶-v_奇 uv 两条边

对于新图我们需要求的就是:
所有从 1 1 1 走向 u 偶 u_偶 u 的路径,对应在原图中就是一条 L L L 为偶数的路径。
所有从 1 1 1 走向 u 奇 u_奇 u 的路径,对应在原图中就是一条 L L L 为奇数的路径。

对于每次查询的 A A A L L L
我们只需要做如下判断
L L L 是偶数:则只要 L > = d i s t [ A ] [ 0 ] ; L>=dist[A][0]; L>=dist[A][0];
L L L 是奇数:则只要 L > = d i s t [ A ] [ 1 ] ; L>=dist[A][1]; L>=dist[A][1];

#include <bits/stdc++.h>
using namespace std;
# define N 100005
# define M 200005
int n, m, query;
int head[N],ver[M],Next[M],tot=-1;
int dist[N][2];
queue<pair<int ,int> > Q;
void ADD(int x,int y){
    ver[++tot] = y;
    Next[tot] = head[x];
    head[x] = tot;
}
void bfs(){
    int x, y, t;
    memset(dist, 0x3f, sizeof(dist));
    Q.push(make_pair(1,0));
    dist[1][0] = 0;
    while (Q.size()){
        int x = Q.front().first;
        int t = Q.front().second;
        Q.pop();
        for (int i = head[x]; ~i; i = Next[i]){
            y = ver[i];
            if (dist[y][t ^ 1] > dist[x][t] + 1){
                dist[y][t ^ 1] = dist[x][t] + 1;
                Q.push(make_pair(y, t ^ 1));
            }
        }
    }
}
int main(){
    int x, y;
    scanf("%d%d%d", &n, &m, &query);
    memset(head, -1, sizeof(head));
    for (int i = 0; i < m; i++){
        scanf("%d%d", &x, &y);
        ADD(x, y);
        ADD(y, x);
    }
    bfs();
    while (query--){
        int A, B;
        scanf("%d%d", &A, &B);
       	if (dist[A][B & 1] <= B){
            puts("Yes");
        } else {
            puts("No");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jnxxhzz/article/details/105347847