cf*3 总结与反思

39 篇文章 0 订阅

E. Binary Deque

题目大意:
01数组,用类似双向队列的出队方式,即从begin、end开始渐渐出队。问最少多少次操作,可以让剩下数组和为s。

解题思路: 因为是双向队列,所以只删除left、right一个方向的数组是不对的。但是每次删除元素要考虑左右等。可以考虑用双指针算法。先求前缀和,快慢指针遍历,当快指针的前缀和元素减去慢指针前缀和元素大于s的时候停止快指针的迭代,之后判断用的操作次数,用总长度减去快慢指针区间就是等于s的操作次数。就这样一直遍历、迭代,找到次数最小的即为答案。

int a[N];
void solve() {
    memset(a, 0, sizeof(a)); // 每次清零
    int n,s; cin>>n>>s;
    rep(i,1,n) {scanf("%d", &a[i]); a[i] += a[i-1]; } // 读入后进行前缀和
 
    int ma(0x3f3f3f3f); // 操作次数
    for(int i = 1, j = 1; i <= n; ++i) { // i -- 慢指针; j -- 快指针;
        while(j < n && a[j + 1] - a[i - 1] <= s) j++; // 快指针迭代,找到大于s的区间;
        if(a[j] - a[i - 1] == s) { // 如果区间和等于s,找最小操作次数
            ma = min(ma, n - (j - i + 1));
        }
    }
    // 不等于 0x3f3f3f3f 表示有答案,否则,无解
    cout<<(ma < INF ? ma : -1)<<"\n";
}

C. Sum of Substrings

题目大意:
一个01字符串,将字符串sisi+1用十进制表示并累加求和。
可进行操作:
将相邻两个字符进行交换。
现,给出最多操作次数,问最小值多少。

解题思路: 观察可以发现,如果字符串第一个元素是1,那么对求和的贡献是10,如果字符串最后一个元素是1,对求和的贡献值是1,其他位置的字符串为1时,贡献是11。(x1, 1x – 1 ,10)。在进行操作的时候,只用考虑begin和end的位置就可以,因为中间怎么交换都是11。可以考虑对字符串第一个位置进行++判断,找到第一个为1的字符位置,对end用–,找到第一个为1的字符位置。

void solve() {
    int n, k; cin>>n>>k;
    string str; cin>>str;
    int len1 = count(str.begin(), str.end(), '1'); // 字符串中有多少个1

    LL sum = 0;
    int le = 0, ri = str.size()-1; 
    while(str[le] == '0') le++; // 从前往后找第一个为1的字符位置
    while(str[ri] == '0') ri--; // 从后往前找第一个为1的字符位置
    sum = le + n - ri - 1; // begin和end查找到需要的操作次数之和
    int ans = len1*11; // 按1都是在中中间
    if(len1 == 0) { // 没有一个‘1’,就是0;return 
        cout<<0<<endl; return ;
    } 
        // 四种情况:
            // begin end 可以完全进行操作
            // end 可以  -- 先end --- 10
            // begin 可以  - 1--
            // 都不可以
    // begin和end的操作次数和小于等于最大操作次数
    if(k >= sum && le != ri) ans -= 11;
    // end的操作次数小于k
    else if(k >= n - ri - 1) ans -= 10;
    // begin的操作次数小于k
    else if(k >= le) ans -= 1;
    cout<<ans<<endl;
} 

C. Elemental Decompress

题目大意:
两个序列的每个位置的最大元素组成的数组已给定。求两个序列。

解题思路: 序列内元素不重复。对于给定的数组,序列元素出现的次数是 0, 1, 2。并且次数为0的元素个数一定跟次数为2的元素个数相同,否则两个序列构不成给定数组。同时,0出现元素和2出现元素一定全部满足出现次数2的元素大于出现次数0的元素。 可以考虑将出现次数为1的元素他们两个序列的位置上的元素相同。0和2的进行互补。

void solve() {
    int n; cin>>n;
    rep(i,1,n) { // 初始化
        a[i] = p1[i] = p2[i] = ha[i] = hs[i] = _cnt[i] = 0;
    }
    rep(i,1,n) {
        cin>>a[i]; // 读入给定数组;
        ha[a[i]] ++; // 每一个元素出现次数 0 1 2;
    }
    rep(i,1,n) {
        if(ha[i] > 2) { // 有大于2的元素次数存在表示不存在两个序列
            puts("NO");
            return ;
        }
    }
    vector<int> zero,two; // 0 2 出现元素读入
    rep(i,1,n) {
        if(ha[i] == 2) two.push_back(i);
        if(ha[i] == 0) zero.push_back(i);
    }
    if(zero.size() != two.size()) { // 0 2 个数不同,“No”
        puts("NO");
        return ;
    }
    // sort 为2 0 分配两个序列
    sort(zero.begin(), zero.end());
    sort(two.begin(), two.end());
    int len = zero.size() -1;
    rep(i,0,len) { // 出现次数2的元素大于出现次数1的元素
        // 元素为2的元素映射的元素为1的元素、
        if(two.at(i) > zero.at(i)) hs[two[i]] = zero[i];
        else {
            puts("NO");
            return ;
        }
    }
    rep(i,1,n) { // 出现次数为1的元素,两个序列的位置元素为该元素
        if(ha[a[i]] == 1) p1[i] = p2[i] = a[i];
    }

    rep(i,1,n) {
        _cnt[a[i]] ++; // 已出现次数
        // 出现一次, 但是数组中有两个该元素,
        if(_cnt[a[i]] == 1 && ha[a[i]] == 2) p1[i] = a[i], p2[i] = hs[a[i]];
        // 出现两次,数组有2个该元素
        // 赋值 互换赋值第一次出现时映射
        if(_cnt[a[i]] == 2 && ha[a[i]] == 2) p2[i] = a[i], p1[i] = hs[a[i]];
    }
    puts("YES");
    rep(i,1,n) cout<<p1[i]<<" "; puts("");
    rep(i,1,n) cout<<p2[i] <<" "; puts("");
}

总结

双指针板子题会,要构造双指针就不会喽,没有找到题思路与双指针算法之间的联系。
做题思维太固定,死板,一个思路想半天不对,不去换思路。以后做题应该多换思路,碰idea。
做题太少,导致做题没有思路,会再多数据结构、算法,没有做大量题刷思路简单算法都很难想到。

寒假每天至少做两道cf题,并写题解和反思。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

golemon.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值