2-4基础算法-离散化 贪心 01背包问题

return x.b - x.a > y.b - y.a;

}
int main()
{
int n, k;
pro arr[N];
cin >> n >> k;
long long sum = 0;
for (int i = 0; i < n; i++) {
cin >> arr[i].a;
sum += arr[i].a;
}
for (int i = 0; i < n; i++) {
cin >> arr[i].b;
}
sort(arr, arr + n, cmp);
for (int i = 0; i < n; i++) {
if (k == 0) {
break;
}
if (arr[i].b > arr[i].a) {
sum -= arr[i].a;
sum += arr[i].b;
k–;
}
}
cout << sum;
}
//也可以定义三个数组运算


6.珠宝的最大交替和  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f9c1b4bf64b6492698dea123ba9409e6.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ece29fb9b4c54e26b7e4cdf8b8428900.png)  
 [评测系统]( )


分析:先求和,然后奇数位找最小的,偶数位找最大的,交换。



#include
#include
#include
#include
using namespace std;
int main()
{
int n;
cin >> n;
vector a(n);
long long sum = 0;
int ji=INT_MAX, ou=INT_MIN;
for (int i = 0; i < n; i++) {
cin >> a[i];
if ( i % 2 == 0) {
sum += abs(a[i]);
ji = min(ji, abs(a[i]));
}
else {
sum -= abs(a[i]);
ou = max(ou, abs(a[i]));
}
}
if(n>1&&ji<ou)
sum = sum - 2 * ji + 2 * ou;
cout << sum;
}


7.小蓝的礼物  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9b9518e1936a4556a3cb06c0a7cde5ab.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/836c326217564d4ab1c7a22813ecd290.png)  
 [评测系统]( )



#include
#include
#include
#include
#include
using namespace std;
int main()
{
priority_queue<int, vector, greater> pq;
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
pq.push(x);
}
long long sum = 0;
int num = 0;
while (pq.size() > 0) {
int x = pq.top();
sum += x;
num++;
if (sum > k) {
sum -= x;
if (x % 2 == 0) {
if (sum + x/2 > k) {
num–;
}
}
else{
if (sum + x/2+1 > k) {
num–;
}
}
cout << num;
return 0;
}
pq.pop();
}
cout << num;
}


8.四个瓷瓶的神秘游戏


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/afcda7c19c0e4ff795d4ec82ba3a18cc.png)


[评测系统]( )


分析:当一个瓶子为空的时候,我们依然可以继续操作。当两个瓶子都为空的时候,我们无法操作。


在通过sort排序后,满足a[0]<=a[1]<=a[2]<=a[3]。我们先通过操作将a[0]变为空,可操作次数为a[0]内的珍珠数。此时最后一个瓶内珍珠数最多,为a[3]+2\*a[0]


在此基础上,对其他瓶进行处理  
 因为a[2]不小于a[1],所以能对a[1]进行的操作都可以对a[2]进行操作,我们假设a[2]和a[3]足够大。我们分别假设在a[0]为0时,a[1]分别取1~9,于是有:


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5d97a1aedce749a5bf1f0125f7f09727.png)


以此类推,我们可以总结出


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c05056f1c8c84d5296c9616ff814d950.png)


可以看出,相差模除为0时,a[1]和a[0]的差值就是a[3]要增加的值;差值模除为其他时,a[3]要增加的值为a[1]和a[0]的差值-1


于是有



#include
#include
using namespace std;
int main() {
long long x;
long long a[4];
for (int i = 0; i < 4; i++) {
cin >> a[i];
}
sort(a, a + 4);
if ((a[1] - a[0]) % 3 == 0)
cout << a[3] + (a[0] * 2) + a[1] - a[0];
else
cout << a[3] + (a[0] * 2) + a[1] - a[0]-1;
}


下面考虑a[3]或a[2]非足够大的情况,拥有最多珍珠的瓶子可能就不是a[3]:  
 在将a[0]变为0的基础上:  
 当瓶子内珍珠数为0111时,一次操作变为2000,此时瓶内最多珍珠数量为2,但按上述分析前两个瓶01时,a[3]数量不变,也就是1,和2差1  
 当瓶子内珍珠数为0112时,一次操作变为2001,此时瓶内最多珍珠数2,a[3]数量不变也正好是2,满足上述推理  
 当瓶子内珍珠数为0113时,一次操作变为2002,此时a[0]=2,a[3]数量不变为3,此时a[3]成为珍珠最多的瓶  
 当瓶子内珍珠数为0222时,一次操作变为4000,此时瓶内最多珍珠数4,但按上述分析前两个瓶02时,a[3]数量+1,也就是3,和4差1  
 以此类推  
 可以看出,当a[3]不是足够大时,或者说,a[3]如果不满足至少比a[2]或a[1]大1时,即a[1]、a[2]、a[3]相等时,a[0]反而成了珍珠最多的瓶  
 我们据此更新代码,考虑a[1]、a[2]、a[3]相等的情况,要在输出基础上+1



#include
#include
using namespace std;
int main() {
long long a[4];
for (int i = 0; i < 4; i++) {
cin >> a[i];
}
sort(a, a + 4);

int x = 0;
if (a[3] == a[1]) {
	x = 1;
}

if ((a[1] - a[0]) % 3 == 0)
	cout << a[3] + (a[0] \* 2) + a[1] - a[0]+x;
else
	cout << a[3] + (a[0] \* 2) + a[1] - a[0] - 1+x;

}


9.鸡哥的购物挑战  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6b46dd8219aa4d53bc6a0818e9f6f58d.png)  
 [评测系统]( )


分析:商品价格有正有负,计算过程中可抵消



#include
#include
#include
using namespace std;
int main() {
priority_queue pq;//大根堆
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
pq.push(x);
}
long long sum = 0;
int num = 0;
int laste;
while (pq.size() > 0&&pq.top()>0) {
sum += pq.top();
laste = pq.top();//记录while最后一个弹出的元素
pq.pop();
num++;
}
if (num < n&& num % 2 != 0) {
if (abs(pq.top()) > laste) {
sum -= laste;
}
else {
sum += pq.top();
}
}
else if (num == n && num % 2 != 0) {
sum -= laste;
}
cout << sum;
}


10.冒险者公会  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5302ea008d8744d1a30699be2705094a.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cb3b5ba964e44d058669b9732599e218.png)


[评测系统]( )


分析:样例说明有一定的误导性,为了更高效的派出冒险者,应该从小到大排序



#include
#include
#include
using namespace std;
const int N = 1e3 + 5;
int cmp(const int a, const int b) {
return a > b;
}
int main() {
int m, n;
cin >> m >> n;
int x[N] = { 0 }, b[N][N] = { 0 };
for (int i = 0; i < m; i++) {
cin >> x[i];
}
sort(x, x + m,cmp);
int maxnum = -1;
for (int i = 0; i < n; i++) {
int k;
cin >> k;
for (int j = 0; j < k; j++) {
cin >> b[i][j];
}
sort(b[i], b[i] + k,cmp);
maxnum = max(maxnum, k);
}
if (m < maxnum) {
cout << “-1”;
return 0;
}
int temp[N] = { -1 };//存储每轮的最大值
int num = 0;
for (int i = 0; i < maxnum; i++) {
for (int j = 0; j < n; j++) {
temp[num] = max(b[j][i], temp[num]);
}
num++;
}
int sum = 0;
num = m-1;
int num2 = maxnum - 1;
while (num!=-1&&num2!=-1) {
if (x[num] < temp[num2]) {
num–;
}
else {
sum += x[num];
num–;
num2–;
}
}
if (num2 == -1)
cout << sum;
else
cout << “-1”;
}


11.明日方舟大作战!  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e0b157f3fbc84e45b4ce7c7fb7a0dfa5.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6704746bc4cc4a41be3715d0d3f22de8.png)


[评测系统]( )


分析:在一轮中所有队员均可上场;对每个队员至多需要一次付费


引入:


## 01背包问题


对于一系列物品,每个物品有两个属性,价值v[i]和重量w[i];一个背包,它有一个最大承重限制W。在0-1背包问题中,每个物品只有两种状态:被选中和未被选中,这就是"0-1"的由来。不同于分数背包问题,我们不能选择物品的一部分,而必须决定是否完整地取用每个物品。0-1背包问题通常通过动态规划来解决。基本思想是使用一个二维数组dp[i][j]来表示当考虑到前i个物品,且背包容量为j时所能得到的最大价值。  
 我们的目标是选择一些物品装入背包,使得这些物品的总价值最大化,同时保证这些物品的总重量不超过背包的承重限制。


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8af90aced84943249d8e3961a9cf6783.png)  
 物品(重量w,价值v)  
 第0行:不考虑任何物品,背包的最大价值为0  
 第0列:当背包重量为0时,无法放入任何物品,最大价值为0  
 表格中的每个数字都表示:当考虑前i个物品时,在背包重量j下,能获得的最大价值


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cc4ceb347f234b2cb898eee277bc2a83.png)  
 对于[1,1]  
 背包容量为1,1号物品重量为2,无法放入背包。  
 即当前物品重量大于背包容量,则背包价值与不放当前物品的价值一致(与只考虑前i-1个物品一致)



if(weight[i]>j){
dp[i][j]=dp[i-1][j]
}


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/512158989b7e44408ceda998eebd659d.png)


对于[2,3]  
 当前物品重量为3≤当前背包容量3,允许放入  
 判断放入后是否能获得更大价值:max(不放入,放入)  
 若不放入:背包的最大价值=在同样的背包容量j下,能获得的最大价值,即dp[i-1][j]  
 若放入:背包内物品的总重量一定已经加上了当前物品的重量和价值,我们找到"不考虑当前物品的价值"和“减去当前物品重量”对应的最优值,再加上当前物品的价值,即可得到结果,即dp[i-1][j-weight[i]]+value[i]



if(weight[i]<=j){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}


对于另一个例子,我们给出完整的代码(注:weight和value数组下标从1开始存放数据,0号索引填充0)  
 这里使用vector表示,也可使用数组



#include
#include
using namespace std;
int knapsack(int W, const vector weight, const vector value) {
int n = weight.size()-1;
//使用vector<vector>创建二维数组
//外层(行)有n + 1个元素(向量)
//对于每个外层元素都有W+1个内层元素(列),并将它们初始化为0
vector<vector> dp(n + 1, vector(W + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= W; j++) {
if (weight[i] <= j) {
dp[i][j] = max(dp[i-1][j], dp[i - 1][j - weight[i]] + value[i]);
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n][W];
}
int main() {
int W = 10;//背包最大容量
vector weight = { 0,1,2,4,2,5 };//每个物品的重量
vector value = { 0,5,3,5,3,2 };//每个物品的价值
cout << knapsack(W, weight, value);//在不超过背包承重限制的条件下,所能选择物品的最大总价值。
}


下面我们再看一个一维dp的例子  
 一维dp每次迭代只保留上一个物品计算的结果,减少了对空间的需求,空间复杂度有所降低。如果问题只要求最大价值而不需要具体的物品列表,一维DP是更好的选择。如果需要知道具体哪些物品被选中,则可能需要额外的逻辑或记录。二维dp可以具体到每个物品和每种容量下的最大价值。


若背包容量10,共有4件物品,(重量,价值)分别为(2,1),(3,3),(4,5),(7,9)


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a6ed609c7cff4a9ba310bcd5e444957d.png)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4416d3da64da418ea1ab6b84145a2efc.png)


处理第一个物品:从右往左填写。第一个物品重量2<10,允许放入,直到j=2。检查价值最大化。



for (int j = W; j >= weight[i]; j–) { //逆序保证了在更新时用到的dp仍然是上一轮(没有考虑当前物品)的结果
dp[j] = max(dp[j], dp[j - weight[0]] + value[0]);
}


处理第二个物品:j=10前两件物品均可放入


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c1db91d13f9a4f2cb33af3dfe815bd5b.png)


j=4时  
 若不放入当前物品,dp[j]=1  
 若放入当前物品,在减去当前重量的情况下决策dp[j - weight[1]] + value[1]=0+3=3



for (int j = W; j >= weight[i]; j–) {
dp[j] = max(dp[j], dp[j - weight[1]] + value[1]);
}


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d60be1bbb9fb428ea2a54f5fda5bb12b.png)  
 以此类推,遍历完所有i得到的dp[10]即为结果


对于另一个例子,我们给出完整的代码(注:weight和value数组下标从0开始存放数据),j是逆序的  
 这里使用vector表示,也可使用数组



#include
#include
using namespace std;

int knapsack(int W, const vector weight, const vector value) {
int n = weight.size();
vector dp(W + 1, 0);

for (int i = 0; i < n; i++) {
    for (int j = W; j >= weight[i]; j--) { //逆序保证了在更新时用到的dp仍然是上一轮(没有考虑当前物品)的结果
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        //max中的dp[j]表示在不加入当前物品i的情况下,物品能达到的最大价值。
        //类似于在二维的基础上去掉了第一个维度
        //dp[i][j] = max(dp[i-1][j], dp[i - 1][j - weight[i]] + value[i]);
    }
}

return dp[W];

}

int main() {
int W = 10;
vector weight = { 1, 2, 4, 2, 5 };
vector value = { 5, 3, 5, 3, 2 };

cout << knapsack(W, weight, value);
return 0;

}


【回到题目】我们使用01背包一维dp找到最大的攻击力,再找到最多血量的敌人,即可计算最小的回合数



#include
#include
#include
#include
using namespace std;
int knapsack(int B, const vector attack, const vector cost) {
vector dp(B + 1, 0);//dp数组记录总费用为j时可获得的最大攻击力
int n = attack.size();
for (int i = 0; i < n; i++) {
for (int j = B; j >= cost[i]; j–) {

css

1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解

js

1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?

1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解

[外链图片转存中…(img-PwU3o81k-1718160973658)]

js

1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?

[外链图片转存中…(img-sKOjOg8w-1718160973661)]

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值