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();
    } 
}
相关推荐
秋夫人25 分钟前
B+树(B+TREE)索引
数据结构·算法
梦想科研社1 小时前
【无人机设计与控制】四旋翼无人机俯仰姿态保持模糊PID控制(带说明报告)
开发语言·算法·数学建模·matlab·无人机
Milo_K1 小时前
今日 leetCode 15.三数之和
算法·leetcode
Darling_001 小时前
LeetCode_sql_day28(1767.寻找没有被执行的任务对)
sql·算法·leetcode
AlexMercer10121 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
Greyplayground1 小时前
【算法基础实验】图论-BellmanFord最短路径
算法·图论·最短路径
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
源代码:趴菜1 小时前
LeetCode63:不同路径II
算法·leetcode·职场和发展
儿创社ErChaungClub2 小时前
解锁编程新境界:GitHub Copilot 让效率翻倍
人工智能·算法
前端西瓜哥2 小时前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法