补题日记(2)

文章讲述了如何利用贪心算法和动态规划解决01背包问题的变形版本,涉及到物品分配策略和背包容量限制的特殊性,以及使用优先队列和哈希表优化求解过程。
摘要由CSDN通过智能技术生成

蓝桥 倒水(变形的01背包)

传送门:1.倒水 - 蓝桥云课 (lanqiao.cn)

首先用一点贪心算法的思想 , 我们知道,在倒水量(0,a)之间获得e的满意度,倒水量(a,c)的区间获得b的满意度,大于等于c获得d的满意度(以上括号都是左闭右开)。那么想要让倒水值尽可能小,我们就可以只选择0,a,c三个倒水量。

通过以上分析,我们有0对应e的满意度 , a对应b的满意度 , c对应d的满意度,这里注意,不同于普通背包的是,在普通的01背包中, 我们可以选择一下物品不取,而在这里所有物品都需要取,也就是我们需要给所有人倒水,所以遍历方式可能就不一样。

首先我们还是正常定义数组

cin>>n>>m;
for(int i =1;i<=n;++i)cin>>a[i]>>b[i]>>c[i]>>d[i];

还是像01背包一样,先遍历物品再遍历背包,但是此时要注意遍历背包时与正常的01背包不同

for(int i=1;i<=n;++i)
    for(int j =m;J>=0;--j)
    

此时是大于等于0了,这就是不同点

那么对于j(也就是倒水量)来说就有三种可能

if(j < a[i])
else if(j >= a[i] && j < c[i])
else 

在这三种可能中,我们来分类说一下

1、第一种情况,j < [I],那么此时就不能倒水了

dp[j] = dp[j] + e[i];

2、第二种情况 , 我们可以选择倒a[i]水或者不倒水

dp[j] = max(dp[j] + e[i] , dp[j - a[i]]+b[i]);

3、第三种情况,我们可以选择不倒水 , 倒a[i],倒c[i]

dp[j]  =max(dp[j]+e[i] , max(dp[j - a[i]]+b[i] , dp[j- c[i]] + d[i]);

那么分析到这里,这道题目也就做完了。

下面请看ac代码

#include <bits/stdc++.h>
using namespace std;
const int N =1e4;
long long dp[N];
int a[N] , b[N] ,c[N],d[N],e[N];
int n,m;

int main(){

  cin>>n>>m;
  for(int i =1;i<=n;++i)cin>>a[i]>>b[i]>>c[i]>>d[i]>>e[i];


    for(int i = 1;i<=n;++i){
		for(int j = m;j>=0;--j){
			if(j < a[i])dp[j] = dp[j] +e[i];
 			else if(j >=a[i] && j <c[i]){
			dp[j] = max(dp[j]+e[i] , dp[j - a[i]] + b[i]);
		}
			 else{
			dp[j] = max(dp[j]+e[i] , max(dp[j - c[i]] + d[i] , dp[j-a[i]]+b[i]));
		}
		
	}
  }
  cout<<dp[m];
  return 0;
}

洛谷 5倍经验日(变形01背包)(与上一题几乎一样,类题)

传送门:洛谷 5倍经验日

话不多说,这题与上题基本一样,前面的输入数组啊之类的就不说了,说一下重点

首先这题依然是所有人都要打也就是所有物品都要取,那么就是

for(int i =1;i<=n;++i)
    for(int j =m;j>=0;--j)

但不同的是这题只有两种可能了,不像上一题有三种/

1、精力够打这个人 也就是

if(j >= c[i])
dp[j] = max(dp[j] ,dp[j - c[i]]+b[i])

2、精力不够了

if(j < c[i]){
    dp[j] = dp[j] + a[i];
}

那么就做完了

下面是ac代码

#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const int N  = 1e3 + 9;
ll a[N] ,b[N] , c[N] , dp[N];
int n,m;
int main(){
	cin>>n>>m;
	for(int i =1;i<=n;++i)cin>>a[i]>>b[i]>>c[i];
	
	for(int i =1;i<=n;++i){
		for(int j =m;j>= 0;--j){
			if(j >= c[i])dp[j] = max(dp[j]+a[i],dp[j-c[i]]+b[i] );
			else dp[j] = dp[j]+a[i];
		}
	}
	
	cout<<dp[m] * 5;
	return 0;
}

洛谷 笨小猴(哈希表 , 优先队列)

传送门:笨小猴

这题用普通的哈希数组就能做,但是我用的是比较高大上(哈哈)的解法,那就是map+优先队列

我们用map对每个数字进行存储

string s;cin>>s;
for(auto num : s) mp[num]++;

那么如何从大到小排序呢,我使用了优先队列,注意的是此时用pair加入对优先队列

priority_queue<pair<int , int >>q;
for(auto [x ,y]:mp){
    q.emplace(y,x);
}

下一步就是如何找出现频率最高的数和最低的数,那么最高频率的数已经被我们的优先队列储存起来了,而且就在第一个,最低频率的数在最后一个

int ma = q.top().first;
while(q.size() !=1){
    q.pop();
}
int mi = q.top().sfirst;

剩下就很容易计算出差值,并且写一个布尔函数判断是否为质数(要注意1,0都不是质数,我就因为这个一开始只拿了50分,后来加上去就ac了),那么本题就做完了

下面是ac代码

#include <bits/stdc++.h>
using namespace  std;

map<int,int>m;
priority_queue<pair<int,int>>q;

bool check(int x){
    if(x == 0 ||x ==1)return false;
	for(int i =2;i<x;++i){
		if((x%i) == 0)return false;
	}
	return true;
}

int main(){
	string s;
	cin>>s;
	for(auto num : s){
		m[num]++;
	}
	for(auto [x,y]:m){
		q.emplace(y,x);
	}
	int ma = q.top().first;
	int mi = 0;
	while(q.size() != 1){
		q.pop();
	}
	mi = q.top().first;
	int c = ma - mi;
	
	if(check(c)){
		cout<<"Lucky Word"<<'\n';
		cout<<c; 
	}
	else {
		cout<<"No Answer"<<'\n';
		cout<<0;
	}
}

洛谷 连续自然数和(数学)

传送门:P1147 连续自然数和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

首先因为是连续的自然数和,我们可以想到等差数列首项加尾项乘项数除二,假设尾项是r,首项是l的话就是  (l+r)(r-l+1)/2 = m(m是题里面给的那个和),转换一下就是 (l + r)(r-l+1) = 2m

所以(l+r)和(r-l+1)是2m的两个因子,那么我们可以先找找2m都有哪些因子,用一个vector数组存储起来

vector<int >x;
for(int i=2;i<2*m;++i){
    if(2*m % i == 0){
       x.push_back(i);
    }
}

此时2m的所有因子就进入了这个数组了。

接下来是本题另一个关键所在:

我们设2m的两个因子为 x 和 y,那么

        l + r =x

        r-l +1 =y

我们可以解出r = (x -y +1)/2; l =x -r;

说明两个因子相减+1是可以被二整除的,那我们可以枚举两个因子

for(auto num1 : x)
    for(auto num2 : x)
        if((num1 - num2 +1)%2)continue; //不能被整除,跳过
        int r = (num1 -num2 +1)/2;
        int l  =num1 -l;

最后我们还要看l和r是否合法,合法的话用数组存起来

vector<pair<int,int>>ans;

if(0 <=l && l<=r &&(l+r)*(r-l+1)==2*m)
    ans.push_back({l,r});

最后是ac代码

#include <bits/stdc++.h>
using namespace std;

int n;

int main(){
	cin>>n;
	vector<int>x;
	for(int i =2;i<2 * n;++i){
		if((2 * n) % i == 0)x.push_back(i);  // i是因子 
	}
	vector<pair<int,int>>ans;
	for(auto num1 : x){
		for(auto num2 : x){
			if((num1 - num2 + 1)%2)continue;
			int l = (num1 - num2 +1) / 2;
			int r = num1 - l;
			
			//判断合法性
			if(0<= l && l <=r &&(r- l +1)*(r + l) == 2* n){
				ans.push_back({l,r});
			} 
		}
	}
	sort(ans.begin() , ans.end());
	for(auto [x ,y] : ans){
		cout<<x<<' '<<y<<'\n';
	}
	
	return 0;
} 

结束了,完结撒花,如果喜欢这篇文章可以点赞支持哦!!!

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值