第3,4周

一. 集美大学“第十五届蓝桥杯大赛”
https://zhuanlan.zhihu.com/p/678902967
【3】20231122
求区间[2023,20231122]范围内的整数出现 1122 子串的整数数量。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int ans=0;
	for(int i=2023;i<=20231122;i++)
	{
		ans+=to_string(i).find("1122")!=string::npos;
	}
	cout<<ans<<endl;
	return 0;
}
  1. to_string(i) 是一个 C++ 标准库函数,用于将整数转换为对应的字符串。接受一个整数作为参数,并返回一个表示该整数的字符串。

 例如:如果你有一个整数变量 int num = 123,你可以使用 to_string(num) 来将它转换为字符串。函数将返回一个包含 "123" 的字符串。(✳只适用于整形数据)

  1. 当你想要在一个字符串中找到一个特定的子字符串时,可以使用 .find("1122") 函数。它接受一个字符串作为参数,并返回一个表示该子字符串在原始字符串中第一次出现的位置的整数值。(✳.find()函数只会返回第一次出现的位置(从0开始索引)。)

 例如,如果你有一个字符串变量 std::string str = "112233445566";,你可以使用 str.find("1122") 来找到子字符串 "1122" 在原始字符串中的位置。如果 "1122" 存在于字符串中,函数将返回一个非负整数,表示子字符串的起始位置。如果子字符串不存在,函数将返回一个特殊的值 std::string::npos。

二. set
set就是集合,STL的set用二叉树实现,集合中的每个元素只出现一次(参照数学中集合的互斥性),并且是排好序的(默认按键值升序排列)
访问元素的时间复杂度是O(log2n)
头文件是#include

常用操作:

点击查看代码
set<int> q;   //以int型为例 默认按键值升序
set<int,greater<int>> p; //降序排列 
int x;
q.insert(x);//将x插入q中
q.erase(x);	//删除q中的x元素,返回0或1,0表示set中不存在x
q.clear();	//清空q
q.empty();	//判断q是否为空,若是返回1,否则返回0
q.size();	//返回q中元素的个数
q.find(x);	//在q中查找x,返回x的迭代器,若x不存在,则返回指向q尾部的迭代器即 q.end()
q.lower_bound(x); //返回一个迭代器,指向第一个键值不小于x的元素
q.upper_bound(x); //返回一个迭代器,指向第一个键值大于x的元素

q.rend();  //返回第一个元素的的前一个元素迭代器
q.begin();  //返回指向q中第一个元素的迭代器

q.end(); //返回指向q最后一个元素下一个位置的迭代器
q.rbegin();	 //返回最后一个元素

set单元素应用

点击查看代码
#include<iostream>
#include<set>
using namespace std;
int main()
{
	set<int> q;  //默认按升序排列 
	q.insert(5);
	q.insert(5);
	q.insert(5);
	cout<<"q.size "<<q.size()<<endl;  //输出 1 ,在set插入中相同元素只会存在一个
	
	q.clear(); //清空set
	cout<<"q.size "<<q.size()<<"\n\n";
	
	q.insert(4);
	q.insert(4);
	q.insert(3);
	q.insert(3); 
	q.insert(2);
	q.insert(1);
	
	cout<<"lower_bound "<<*q.lower_bound(3)<<endl; //返回3 
	cout<<"upper_bound "<<*q.upper_bound(3)<<"\n\n"; //返回4 
	
	set<int>::iterator i;
	for( i=q.begin();i!=q.end();i++)  //set的遍历 
		cout<<*i<<" ";				  //输出1 2 3 4,可见自动按键值排序 
	cout<<endl;
	
	q.erase(4);  //删除q中的 4 
	
	for(i=q.begin();i!=q.end();i++)  //再次遍历set 只输出 1 2 3 
		cout<<*i<<" ";
	cout<<"\n\n"; 
	
	
	set<int,greater<int>> p;  //降序排列 
	p.insert(1);
	p.insert(2);
	p.insert(3);
	p.insert(4);
	p.insert(5);
	for(i=p.begin();i!=p.end();i++)
		cout<<*i<<" ";
	cout<<endl;
	return 0;
}

set例题示范:
洛谷:P4305 [JLOI2011] 不重复数字

点击查看代码
#include<bits/stdc++.h>
using namespace std;
vector<int> kkk(const vector<int>& a)
{
	set<int> uniqueNums;
	vector<int> r;
	
	for (int num : a)
	{
		if (uniqueNums.find(num) == uniqueNums.end())
		{
			uniqueNums.insert(num);
			r.push_back(num);
		}
	}
	return r;
}
int main()
{
	int t;
	cin>>t;
	for(int i=0;i<t;i++)
	{
		int n;
		cin>>n;
		vector<int> a(n);
		for(int j=0;j<n;j++)
		{
			cin>>a[j];
		}
		vector<int> r=kkk(a);
		
		for(int j=0;j<r.size();j++)
		{
			cout<<r[j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

01背包问题
题目
有N件物品和一个容量为V的背包。放入第i件物品花费的费用是c[i],得到的价值是w[i],求将哪些物品装入背包可使价值总和最大。(这里的费用可理解为将某个物品装入背包所付出的空间上的代价,c[i]视作cost,w[i]视作worth)
f[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值

状态转移方程:

“将前i件物品放入容量为j的背包中”这个子问题,如果只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题,如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为j的背包中”,最大价值为f[i-1][j];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为j-c[i]的背包中”,此时能获得的最大价值就是f[i-1][j-c[i]]再加上同过放入第i件物品获得的价值w[i]。

优化空间复杂度
以上方法的时间和空间复杂度均为O(vn)(在动态规划的状态转移方程中,我们需要遍历每个物品和每个背包容量,并计算出在背包容量为j的情况下,放入物品i是否能够获得更优的解。因此,我们需要遍历n个物品和背包容量从0到V的所有可能取值,共计Vn个状态),其中时间复杂度最优,空间复杂度看可以优化到O(v)。

先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1...N,每次算出来二维数组f[i][0...V]的所有值。那么,如果只用一个数组f[0...V],能不能保证第i次循环结束后f[j]中表示的就是我们定义的状态f[i][j]呢?f[i][j]是由f[i−1][j]和f[i−1][j−c[i]]两个子问题递推而来,能否保证在推f[i][j]时(也即在第i次主循环中推f[j]时)能够得到f[i−1][j]和f[i−1][j−c[i]]的值呢?事实上,这要求在每次主循环中我们以j=V...0的顺序推f[j],这样才能保证推f[j]时f[j−c[i]]保存的是状态f[i−1][j−c[i]]的值。

代码如下:
​for (int i = 1; i <= n; i++)
  for (int j = V; j >= 0; j--)
    f[j] = max(f[j], f[j - c[i]] + w[i]);

其中的f[j] = max(f[j], f[j - c[i]] + w[i])就相当于转移方程,因为现在的f[j−c[i]]就相当于原来的f[i−1][j−c[i]]。如果将V VV的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][j]由f[i][j−c[i]]推知,与本题意不符。

下面是另一段代码:
​​for (int i = 1; i <= n; i++)
  for (int j = V; j >= c[i]; j--)
    f[j] = max(f[j], f[j - c[i]] + w[i]);
这个过程里的处理与前面给出的代码有所不同。前面的示例程序写成j=V...0是为了在程序中体现每个状态都按照方程求解了,避免不必要的思维复杂度。而这里既然已经抽象成看作黑箱的过程了,就可以加入优化。费用为c[i]的物品不会影响状态f[0...c[i]−1],这是显然的,因为该物品无法装入背包。

初始化问题
我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求"恰好装满背包"时的最优解,有的题目则并没有要求必须把背包装满。这两种问法的区别是在初始化的时候有所不同。
如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1...V]均设为−∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解(其实f[1...j...n]分别对应着恰好装满容量为j的背包的最优解)。
如果并没有要求必须把背包装满,而是只希望所装物品价值尽量大,初始化时应该将f[0...V]全部设为0(此时f[j]则表示不必装满容量为j的背包的最优解)。
可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的“无”“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是−∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。这个小技巧完全可以推广到其它类型的背包问题。
根本区别在于转移。如果用物品根本无法凑出某个容量,设该容量为j,则f[j]永远不会通过f[0]=0得到正值,因为f数组的转移(变量j的跳跃)是用V−c[i]完成的,f[j]只会一直在取max中获得负值(加了w[i]的负值)。

每一格代表当前背包容量下的最大价值,对于每个物品都存在两种情况,选或不选,不选的话看每一格的上一行(蓝),作为价值1,选的话,看当前最大容量减去物体体积后的容积(黄),看新容积下的最大价值,再加上当前物体价值,黄蓝相比,得到结果。

洛谷P1048 [NOIP2005 普及组] 采药

点击查看代码
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int w[105],val[105];
int dp[105][1005];
int main() {
	int t,m;
	cin>>t>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>w[i]>>val[i];
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=t;j>=0;j--)
		{
			if(j>=w[i])
			{
				dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);
			}
			else 
			{
				dp[i][j]=dp[i-1][j];
			}
		}
	}
	cout<<dp[m][t];
	return 0;
}

P8742 [蓝桥杯 2021 省 AB] 砝码称重

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 105, maxv = 1e5 + 5;
int n, a[maxn], f[maxn][maxv], sum, ans;
int main()
{
	cin >> n;
	for( int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
		sum += a[i];
	}
	f[0][0] = 1;
	for( int i = 1; i <= n; i ++ )
	{
		for( int j = sum; j >= 0; j -- )
		{
			f[i][j] |= f[i - 1][j];
			f[i][j] |= f[i - 1][abs( j - a[i] )];
			if( j + a[i] <= sum )
				f[i][j] |= f[i - 1][j + a[i]];
		}
	}
	for( int i = 1; i <= sum; i ++ )
		ans += f[n][i];
	cout << ans;
}

02完全背包问题
有N NN种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
按上图,因为可以选多次,所以每次从同一行中去比较。

1.考虑到第i种物品最多选V/c[i]件,于是可以把第i种物品转化为V/c[i]件费用及价值均不变的物品,然后求解这个01背包问题。
2.把第i种物品拆成费用为c[i]2^k ,价值为w[i]2^k 的若干件物品,其中k满足c[i]*2^k <=V。因为不管最优策略选几件第i种物品,总可以表示成若干个2^k 件物品的和。这样把每种物品拆成log(V/c[i])件物品。

P8806 [蓝桥杯 2022 国 B] 搬砖

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+4;
const int V=2e4+4;
const int inf=0x3f3f3f3f;
ll n,a[N],b[N],id[N];
ll maxn,dp[V];
bool cmp(ll x,ll y){
	return a[x]+b[x]<a[y]+b[y];
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
		id[i]=i;
	}
	sort(id+1,id+n+1,cmp);
	memset(dp,-inf,sizeof(dp));
	dp[0]=0;
	for(int i=1;i<=n;i++){
		for(int j=a[id[i]]+b[id[i]];j>=a[id[i]];j--){
			dp[j]=max(dp[j],dp[j-a[id[i]]]+b[id[i]]);
		}
	}
	for(int j=1;j<=2e4;j++){
		maxn=max(maxn,dp[j]);
	}
	cout<<maxn;
}

P8651 [蓝桥杯 2017 省 B] 日期问题

点击查看代码
#include<bits/stdc++.h>
using namespace std; 
char a[20];
int M[20]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int main()
{
	int l,i,j,k,x,y,z;
	scanf("%s",a);
	l=strlen(a);
	x=(a[0]-48)*10+a[1]-48;
	y=(a[3]-48)*10+a[4]-48;
	z=(a[6]-48)*10+a[7]-48;
	for(i=1960;i<=2059;i++)
	{
		if(i%400==0||(i%4==0&&i%100!=0))	//判断闰年
			M[2]=29;
		for(j=1;j<=12;j++)
		{
			for(k=1;k<=M[j];k++)
				if((x==i%100&&y==j&&z==k)||(z==i%100&&x==j&&y==k)||(z==i%100&&y==j&&x==k))
				{
					cout<<i<<"-";
					if(j<10)
						cout<<0;
					cout<<j<<"-";
					if(k<10)
						cout<<0;
					cout<<k<<endl;
				}	
		}
		M[2]=28;
	}
}

P8647 [蓝桥杯 2017 省 AB] 分巧克力

点击查看代码
#include<bits/stdc++.h>
using namespace std;

int n,k;
int h[100002];
int w[100002];

bool check(int x){//判断是否大于等于 k
	int s=0;
	for(int i=1; i<=n; i++) s+=(h[i]/x)*(w[i]/x);
	if(s>=k) return true;
	else return false;
}

int main(){
	cin>>n>>k;
	for(int i=1; i<=n; i++) cin>>h[i]>>w[i];
	int l=1,r=100002;
	while(l<r){//二分
		int mid=(l+r+1)/2;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l;
	return 0;
}

F - kita 买礼物
https://codeforces.com/gym/102897/problem/F

点击查看代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

typedef pair<int,int> PII;
typedef long long ll;
const int N=3e6+5,M=1e9+7;
int n,m,v[N],b[N],a[100005];
bool f[N];
int main(){
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i];
	int c=0;
	for(int i=1;i<=n;++i)
	{
		int k,s=1;
		cin>>k;
		while(s<=k)
		{
			c++;
			v[c]=a[i]*s;
			k-=s;
			s*=2;
		}
		if(k>0)
		{
			c++;
			v[c]=a[i]*k;
		}
	}
	n=c;
	f[0]=true;
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=1;--j)
		{
			if(j>=v[i])
			{
				f[j]|=f[j-v[i]];
			}
		}
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		ans+=f[i];
	}
	cout<<ans;
	return 0;
}
posted @   miao-jc  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示