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();
    } 
}
相关推荐
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_3 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子3 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡4 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码4 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7244 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活4 小时前
理解支持向量机
算法·机器学习·支持向量机
大山同学5 小时前
第三章线性判别函数(二)
线性代数·算法·机器学习