#6039. 「雅礼集训 2017 Day5」珠宝

本文介绍了一种使用决策单调性分治DP算法解决背包问题的方法,通过将相同成本的物品按价值排序并利用决策单调性进行优化,实现O(cklogk)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
Miranda 准备去市里最有名的珠宝展览会,展览会有可以购买珠宝,但可惜的是只能现金支付,Miranda 十分纠结究竟要带多少的现金,假如现金带多了,就会比较危险,假如带少了,看到想买的右买不到。展览中总共有 N 种珠宝,每种珠宝都只有一个,对于第 i 种珠宝,它的售价为 Ci​ 万元,对 Miranda 的吸引力为 Vi​ 。Miranda 总共可以从银行中取出 K万元,现在她想知道,假如她最终带了 i 万元去展览会,她能买到的珠宝对她的吸引力最大可以是多少?
输入格式
第一行两个整数 N、K。
接下来 N行,每行两个整数 Ci、Vi​
输出格式
输出一行 K个整数,对于第 i个数,表示假如 Miranda 带了 i 万元现金,她能买到的珠宝对她的吸引力最大可以是多少

对于 20% 20% 20% 的数据,N,K≤10000 N, K \leq 10000 N,K10000
对于另外 20% 20% 20% 的数据,Ci=Vi C_i = V_i Ci=Vi
对于 100% 100% 100% 的数据,1≤N≤1000000,1≤K≤50000,1≤Ci≤300,0≤Vi≤109 1 \leq N \leq 1000000, 1 \leq K \leq 50000, 1 \leq C_i \leq 300, 0 \leq V_i \leq 10 ^ 9 1N1000000,1K50000,1Ci300,0Vi109

终于把这道初三时的历史遗留问题解决了。。。。。
容易想到c一样的一起转移,发现c一样,v大的应该先选,所以选的一定是最大的那几个,又发现最大的x个的价值在x变大的时候是增长率递减的。。这就是这个dp具有(局部)决策单调性的一个明显标志。。
所以针对每一种c一起用决策单调性的分治dp方法(单调队列也行)转移就可以把这种奇葩的依赖背包问题用klogk的时间复杂度解决,总复杂度O(cklogk)
AC Code:

#include<bits/stdc++.h>
#define maxk 50005
#define LL long long
using namespace std;
 
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
inline void read(int &res){ char ch;for(;!isdigit(ch=getc()););for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0'); }
int n,k,siz[305];
vector<LL>sum[305];
LL dp[2][maxk],g[2][maxk];
int now=1,pre=0;
 
void solve(int l,int r,int ql,int qr,vector<LL>&sum,int &siz)
{
    if(ql > qr || l > r) return;
    int Minloc = -1 , mid=(ql+qr)/2;LL val=0;
    for(int i=max(l,mid-siz);i<=r && i<mid;i++)
        if(Minloc==-1||g[0][i]+sum[mid-i-1] > val)
            val = g[0][i]+sum[mid-i-1] , Minloc = i;
    g[1][mid] = val;
    if(Minloc == -1) Minloc = l;
    solve(l,Minloc,ql,mid-1,sum,siz),solve(Minloc,r,mid+1,qr,sum,siz);
}
 
inline bool cmp(const int &u,const int &v){ return u > v; }
 
int main()
{
    read(n),read(k);
    for(int i=1,c,v;i<=n;i++) read(c),read(v),sum[c].push_back(v),siz[c]++;
    for(int i=1;i<=300;i++) 
        if(siz[i]){
            sort(sum[i].begin(),sum[i].end(),cmp);
            for(int j=1;j<siz[i];j++) sum[i][j] += sum[i][j-1];
            for(int j=0;j<i;j++) 
            {
                int p=j,bk=0;
                for(;p<=k;p+=i,bk++)
                    g[0][bk] = dp[pre][p];
                solve(0,bk-1,0,bk-1,sum[i],siz[i]);
                for(p=j,bk=0;p<=k;p+=i,bk++)
                    dp[now][p] = max(g[0][bk] , g[1][bk]);
            }
            swap(now,pre);
        }
    for(int i=1;i<k;i++)
        printf("%lld ",dp[pre][i]);
    printf("%lld\n",dp[pre][k]);
}
### 关于雅礼集训 2017 Day1 的题目及解析 #### 题目概述 根据已知引用内容[^3],雅礼集训 2017 Day1 的核心问题是关于矩阵操作的优化问题。给定一个 \(n \times m\) 的字符矩阵,其中 `#` 表示黑色格子,`.` 表示白色格子。目标是小化将整个矩阵变为全黑所需的步数。 --- #### 解析与算法思路 ##### 输入描述 输入的第一行为两个整数 \(n\) 和 \(m\),分别代表矩阵的行数和列数。接下来 \(n\) 行每行包含长度为 \(m\) 的字符串,表示矩阵的内容。 ##### 输出描述 输出小的操作次数使得整个矩阵变成全是黑色格子的状态。如果没有可行方案,则输出 `-1`。 --- ##### 算法设计 1. **可行性判断** 如果初始矩阵没有任何黑色格子 (`#`) 存在,则无法通过任何有限次操作使矩阵变黑,因此直接返回 `-1`[^4]。 2. **计算少步数** 对于每一行,定义两种可能的操作方式: - 将该行全部涂黑。 - 不改变该行状态,仅依赖后续列操作来覆盖剩余白格。 同样地,对于每一列也存在类似的策略选择。终的目标是综合考虑行列操作的影响,找到全局优解。 3. **动态规划或贪心求解** 使用简单的遍历方法统计各行列中的黑白分布情况,并基于此决定佳行动顺序。特别注意边界条件处理以及特殊情况下的额外开销评估。 ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e3 + 5; int n, m, h[MAXN], l[MAXN]; char s[MAXN][MAXN]; int main(){ cin >> n >> m; bool has_black = false; for (int i = 1; i <= n; ++i){ cin >> (s[i]+1); for (int j = 1; j <= m; ++j){ if (s[i][j] == &#39;#&#39;){ has_black = true; h[i]++; l[j]++; } } } if (!has_black){ cout << "-1"; return 0; } int res = INT_MAX; for (int i = 1; i <= n; ++i){ res = min(res, m - h[i] + !l[i]); } int extra_cost = 0; for (int j = 1; j <= m; ++j){ if (l[j] != n) extra_cost += 1; } cout << res + extra_cost; } ``` 上述代码实现了基本逻辑框架,包括读取数据、初步分析是否存在解决方案的可能性以及后一步汇总总成本的过程[^3]。 --- #### 复杂度分析 时间复杂度主要取决于两次嵌套循环扫描整个矩阵所需的时间量级 O(n*m),空间消耗同样维持在线性范围内 O(n+m)。 --- #### 注意事项 - 当前实现假设所有测试实例均满足合理范围内的尺寸规格;实际应用时需增加更多健壮性的错误检测机制。 - 结果验证阶段应充分考虑到极端情形比如完全空白或者满布障碍物等情况是否被妥善处置。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值