寒假每日一题(提高组)Week 3

1262. 鱼塘钓鱼

分析

最开始在1号鱼塘

在去做的时候, 我们可以先去枚举下最远走到哪个鱼塘, 为什么要这样枚举呢

我们发现在每个鱼塘钓到鱼的数量, 只取决于在这个鱼塘钓鱼的总时间
因此不可能出现折返的情况(先往前走, 钓几分钟, 再回来钓❌)

如果出现折返的情况的话, 我可以改进方案, 直接在第1个鱼塘钓满可以钓的时间, 再去第2个鱼塘钓鱼, 这样我们总时间就会变小

因此不可能往回走, 所以会往前走

所以我们枚举最远到达哪个鱼塘, 那么路上所花的时间就确定了
因此总共的钓鱼时间就确定了, = T(总时间) - 路上的时间

有了总共钓鱼时间, 比如是100
相当于是求每一个鱼塘对应的序列, 每个序列的话, 可以取出若干个数, 总共要取100个数, 在这种情况下, 问我们所有取的数的总和最大是多少

所以总和最大, 就意味着我们要取所有序列 最靠前的那些数

在这里插入图片描述

所以剩下的问题就转化为这些单调的数列的最大的前若干个数是多少, 那么就可以用多路归并排序

在这里插入图片描述

所以这题考察点就是: 枚举 + 贪心 + 多路归并

时间复杂度的话, 我们先要枚举下鱼塘n,
总时间的话,题目最大给了1000, 因此总时间复杂度nT

多路归并的话, 可以用堆来做, 也可以用循环来做, n比较小, 所以直接用循环

总时间复杂度n^T = 10 0 2 ∗ 1000 100^2 * 1000 10021000 = $10^7 $

code

//1.因为在鱼塘间来回走动会减少钓鱼的总时间,因此只需要确定最远走到哪里,就可以计算出钓鱼的总时间

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110;

int a[N], d[N], l[N], spend[N];//spend表示在该鱼塘花的时间

int get(int k){
    return max(0, a[k] - d[k] * spend[k]);
}

int work(int n, int T){// T表示总钓鱼时间
    int res = 0;
    memset(spend, 0, sizeof spend);
    // 多路归并, n条路, 取前T个数, 所以先枚举T, 再枚举每条路
    
    for (int i = 0; i < T; i ++ ){// 枚举T分钟内, 每分钟可以选的最大数
        int t = 1;
        for (int j = 1; j <= n; j ++ ) // 枚举每个鱼塘
            if (get(j) > get(t))
                t = j;
        res += get(t); // 先加上能钓到的鱼的数量
        spend[t] ++; // 钓完后, 该鱼塘钓鱼时间 ++, 顺序不能倒
    }
    return res;
}

int main(){
    int n, T;
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    for (int i = 1; i <= n; i ++ ) cin >> d[i];
    for (int i = 2; i <= n; i ++ ){
        cin >> l[i];
        l[i] += l[i - 1];//前缀和记录走到该点的总时间
    }
    
    cin >> T;
    int res = 0;
    for (int i = 1; i <= n; i ++ )//枚举最远走到哪一个点
        res = max(res, work(i, T - l[i]));
    
    cout << res << endl;
    
    return 0;
}

312. 乌龟棋

分析

发现一共有120张卡片, 每张卡片的数量不会超过40, 因此可以用4维, 表示当前每张卡片用了多少张

这样状态数量 4 0 4 = 2560000 40^4 = 2560000 404=2560000, 用f[a][b][c][d]表示

考虑下f[a][b][c][d]怎么求, 以最后一张用了什么类型卡片为分界点
然后看下其中每一类的最大值是多少, 最终取max, 就是总共最大值

以最后一张用了c卡片为例:
如果当前跳到了第s个位置, 如果最后一张卡片用了c, 那么倒数第2步一定在s - 3的位置
那么前面这个状态就是f[a][b][c - 1][d] + w_s

在这里插入图片描述
状态数量 4 0 4 40^4 404, 状态转移的时间复杂度O(1), 其实没有这么大, 因为总共只有120张, 4个数的和是120, 4个数的乘积最大的话, 那么4个数得相等, 3 0 4 30^4 304

code

#include <iostream>
using namespace std;
const int N = 41, M = 360; // 题目给了总长度350
int f[N][N][N][N];
int n, m;
int w[M];
int b[N];

int main(){
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%d", &w[i]);
    
    for (int i = 0; i < m; i ++ ){ // 注意这里题目是输出每张卡片的类型, 所以先读入卡片的类型x
        int x;
        scanf("%d", &x);
        b[x - 1] ++ ;
    }
    
    for (int A = 0; A <= b[0]; A ++ )
        for (int B = 0; B <= b[1]; B ++ )
            for (int C = 0; C <= b[2]; C ++ )
                for (int D = 0; D <= b[3]; D ++ ){
                    int score = w[A * 1 + B * 2 + C * 3 + D * 4];
                    int& v = f[A][B][C][D];
                    v = score; // 因为有可能4种dp的方案都不存在, 所以先用score初始化v, 比如第1个格子, 就不存在4种方案
                    if (A) v = max(v, f[A - 1][B][C][D] + score);
                    if (B) v = max(v, f[A][B - 1][C][D] + score);
                    if (C) v = max(v, f[A][B][C - 1][D] + score);
                    if (D) v = max(v, f[A][B][C][D - 1] + score);
                }
                
    cout << f[b[0]][b[1]][b[2]][b[3]] << endl;
    
    return 0;
    
}

211. 计算系数

分析

C a b = C a − 1 b + C a − 1 b − 1 C_a^b = C_{a - 1}^b + C_{a - 1}^{b - 1} Cab=Ca1b+Ca1b1
推到思路: 固定一个数, 不选这个数的话, 那么就是从a - 1个中选b个;选这个数的话, 就是剩余a - 1中选b - 1个数

code

#include <iostream>
using namespace std;

const int N = 1010, MOD = 10007;

int C[N][N];

int power(int a, int b){
    a %= MOD;
    int res = 1;
    while (b -- ) res = res * a % MOD;
    
    return res;
}

int main(){
    int a, b, k, n, m;
    cin >> a >> b >> k >> n >> m;
    
    for (int i = 0; i <= k; i ++ )
        for (int j = 0; j <= i; j ++ )
            if (!j) C[i][j] = 1;
            else C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
            
    cout << C[k][n] * power(a, n) % MOD * power(b, m) % MOD << endl;
    
    return 0;
}

496. 机器翻译

分析

在这里插入图片描述

code

#include <iostream>
#include <queue>
using namespace std;
const int N = 1010;

queue<int> q;
bool st[N];
int n, m;

int main(){
    scanf("%d%d", &m, &n);
    
    int res = 0;
    while (n -- ) {
        int x;
        cin >> x;
        if (!st[x]) {
            if (q.size() == m) {
                st[q.front()] = false;
                q.pop();
            }
            q.push(x);
            st[x] = true;
            res ++;
        }
    }
    
    cout << res << endl;
    
    return 0;
}

148. 合并果子

分析

用堆来做

code

#include <iostream>
#include <queue>
using namespace std;
priority_queue<int, vector<int>, greater<int>> heap;
int n;

int main(){
    scanf("%d", &n);
    while (n -- ) {
        int x;
        scanf("%d", &x);
        heap.push(x);
    }
    
    int res = 0;
    while (heap.size() > 1){
        int a = heap.top(); heap.pop();
        int b = heap.top(); heap.pop();
        a += b;
        heap.push(a);
        res += a;
    }
    
    cout << res << endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值