DP训练(codeforces)

第一题 D - Earn or Unlock

问题简述

  • n卡牌从上往下放,开局有一次操作次数。(操作类型如下):
  • 1.将卡牌的值转化为操作次数
  • 2.获取卡牌的值
  • 问最多能获取卡牌的值。

思路

  • dp背包

    我们可以清楚的知道取了前i个卡牌时获得的所有卡牌的值,并且知道一定消耗了i - 1次操作 所以当取了前面i个时消耗了i - 1次操作机会,获得了前i个卡牌的值之和减(i - 1) 所以直接用背包确定在前i个时是否能精准取到(为什么是精准呢,因为可以贪心的认为取的越多越好)

  • 但是单纯的用背包是不可以应用在当前的1e5的复杂度上的,所以可以用二进制bitset来优化背包的复杂度

代码

ini 复制代码
**include <bits/stdc++.h> 
using namespace std;
#define int long long
const int N = 3e5 + 5;
const int mod = 1e9 + 7;


int a[N];
bitset<N>dp;
void solve() {
    int n;
    int sum = 0;
    int ans = 0;
    
    cin >> n;    
    for(int i = 0;i < n;i++) {
        cin >> a[i];
    }
    n *= 2;//其实是n + n

    dp[0] = 1; // 初始化dp
    for(int i = 0;i < n;i++) {
        dp |= (dp << a[i]); // 二进制背包 ,优化复杂度 ,取第i个值用作操作一
        sum += a[i];//前缀
        if(dp[i] == 1) {//当背包刚好能取i时,有i的值被转化为了
            ans = max(ans , sum - i);   
            dp[i] = 0;//同时因为是刚好取到i,所以当不取时结束
        }
    }
    cout << ans << "\n";


}

signed main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
 
    int _ = 1;
    // cin>>_;
    while(_--)
    {
        solve();
    } 
}

第二题 F - Divide, XOR, and Conquer

问题简述

给定n个数ai,ai<=2^60

将这n个数进行以下操作。

选择第k个位置1<=k<n,令x=a1^a2^...^ak,y=a[k+1]^a[k+2]^...^a[n]

  • 如果x>y,保留a1,a2,...,ak
  • 如果x<y,保留a[k+1],a[k+2],...,a[n]
  • 如果x==y,保留前面或者后边都可。

问,对于1<=i<=n,能否通过上述操作,保留剩下第i个元素。、

思路

已知对于一段区间异或[l ,r] ,令s为此区间的异或和,则有以下两种情况: 令k , x , y如问题简述所述

1.s = 0 则x == y , 任意x 或 y区间都可以删 2.s != 0 ,则存在x (或y) 的最高位与区间的异或和最高位相同时x > y(y > x)

(请读者细品为什么)

然后就是直接枚举长度从大到小,定义两个数组st[i]为以i为左边界的区间最高位的集合,ed为右区间 (区间dp)

代码

ini 复制代码
#include <bits/stdc++.h> 
using namespace std;
#define int long long
const int N = 3e5 + 5;
const int mod = 1e9 + 7;
const int M = ((int)1 << 61) - 1;

int a[N];

int hb(int x) {//获取最高位的值15 -> 8
    if(!x) return M;
    else {
        return (int)1 << (63 - __builtin_clzll(x));
    }
}

void solve() {
    int n ;
    cin >> n;
    vector<int>s(n + 1);//前缀和
    for(int i = 1;i <= n;i++) cin >> a[i] , s[i] =(s[i - 1] ^ a[i]);

    if(n == 1) {cout << 1 << "\n";return;}
    
    
    vector<int>st(n + 1) , ed(n + 1);
    st[1] = ed[n] = hb(s[n]); //初始化
    
    for(int len = n;len >= 1;len--) {
        for(int l = 1;l + len - 1 <= n;l++) {
            int r = l + len - 1;
            int sum = (s[r] ^ s[l  - 1]);//区间异或和

            int msk = (sum & st[l]) | (sum & ed[r]);
            //记录区间是否合法,用之前说的方法
            //如果以l为左边界或以r为右边界且包含了该区间的大区间存在最高位与sum且了那么该区间合法
            //说明该区间可以被这个 最高位且sum 的大区间给用操作减去一个小区间获取
            int flag = 0;
            if(msk or st[l] == M or ed[r] == M) {
                st[l] |= hb(sum) , ed[r] |= hb(sum);//更新l , r的最高位集合
                flag = 1;
            }
            if(len == 1) cout << flag;
        }
    }
    cout << "\n";
}

signed main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
 
    int _ = 1;
    cin>>_;
    while(_--)
    {
        solve();
    } 
}
相关推荐
泉崎5 分钟前
11.7比赛总结
数据结构·算法
你好helloworld6 分钟前
滑动窗口最大值
数据结构·算法·leetcode
AI街潜水的八角1 小时前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
白榆maple1 小时前
(蓝桥杯C/C++)——基础算法(下)
算法
JSU_曾是此间年少1 小时前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋2 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
咕咕吖3 小时前
对称二叉树(力扣101)
算法·leetcode·职场和发展
九圣残炎3 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
lulu_gh_yu3 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
丫头,冲鸭!!!4 小时前
B树(B-Tree)和B+树(B+ Tree)
笔记·算法