Codeforces 893 D Credit Card 贪心 思维

  题目链接: http://codeforces.com/contest/893/problem/D

  题目描述: 每天晚上一个数值a,如果a>0表示今天收入a元,<0表示亏损a元,=0表示去检验自己的卡。每天早上(在前者之前)可以去充卡,可以随便充,但是充完之后不能大于上限d。每一次去查卡的时候,卡上的钱不能是个负数,任何时刻如果卡上的钱大于d了就视为不合法,直接输出-1。问最少充几次卡可以保证n天完全合法。

   解题思路: 一开始自己是知道只在查卡的时候充卡就可以了, 然后我是找到与(d相近的s)最小距离的那个下标, 然后0的时候加上去, 在第20组测试样例的时候T掉了, 然后这道题就弃了, 看了题解才知道只需要维护两个区间, 一个是剩余最大, 一个是最小

  我的超时的原因是我算清了每一步都需要加上多少为了取得这个值复杂度加上了O(n) , 所以其实维护的这个区间的作用就是我使得每一次查卡充卡的时候都冲到了d, 同时又记录了如果我不需要充卡的时候会不会超过d, 这样在每次充卡的时候在++ cnt就算出了答案

  代码: 

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <list>
#include <iterator>
#include <cmath>
#include <cstring>
using namespace std;

const int maxn = 1e5+10;
int a[maxn];
const int INF = 0x3fffffff;
int n, d;

int find_index(int s, int id, int & min_value) {
    int min_d = INF;
    int ret = -1;
    for(int i = id+1; i <= n; i++) {
        s += a[i];
        if(d-s < min_d) {
            min_d = d-s;
            ret = i;
        }
    }
    min_value = min_d;
    return ret;
}

int main() {
    //freopen("in.txt", "r", stdin);    
    scanf("%d%d", &n, &d);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    int s = 0;
    for(int i = 1; i <= n; i++) {
        s += a[i];
        if(s > d) {
            printf("-1\n");
            return 0;
        }
    }
    s = 0;
    int cnt = 0;
    int res = 0;
    while(cnt < n) {
        int pre_cnt = cnt+1;
        int min_value;
        cnt = find_index(s, cnt, min_value);
        int flag = 1;
        for(int i = pre_cnt; i <= cnt; i++) {
            s += a[i];
            if(a[i] == 0 && s < 0 && flag == 1) {
                s += min_value; 
                flag = 0;
                res ++;
            }
            if(a[i] == 0 && s < 0 && flag == 0 ) {
                printf("-1\n");
                return 0;
            }
        }
    }
    printf("%d\n", res);            
    return 0;
}
My TLE code
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <list>
#include <iterator>
#include <cmath>
#include <cstring>
using namespace std;

const int maxn = 1e5+10;
int a[maxn];
const int INF = 0x3fffffff;
int n, d;

int main() {
    scanf("%d%d", &n, &d);
    for(int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int flag = 1;
    int top, bottom;
    int res = 0;
    top = bottom = 0;
    for(int i = 0; i < n; i++) {
        if(a[i] == 0) {
            if(top < 0) {
                res++;
                top = d;            
            }
            bottom = max(0, bottom);
        }
        else {
            top += a[i];
               bottom += a[i];
            top = min(top, d);
            if(bottom > d) {
                flag = -1;
                break;
            }
        }
    }
    if(flag == 1) {
        printf("%d\n", res);
    }
    else {
        printf("-1\n");
    }
    return 0;
}
View Code

  思考: 自己考虑问题还是不够全面 啊, ?

转载于:https://www.cnblogs.com/FriskyPuppy/p/7911345.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值