蓝桥杯算法思路总结

枚举

小蓝的漆房
在这里插入图片描述
分析:由于题目最后的结果是相同的颜色,所以我们可以去寻找,把所有房子刷成这个颜色所需要的时间,然后在这里面找一个最小值
1.收集所有颜色
可以用set集合,然后遍历这个集合,试一下每种颜色的最少天数是多少
2.刷色
我们循环的逻辑是,如果这个房子是那个当前给定的颜色,那么我们就不要刷了,如果不是,那我们就一下刷k个,这样可以保证我们速度最快,一点不浪费,最后看刷到最后的天数找一个最小值。
代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,k;
		cin>>n>>k;
		vector<int> colorHouse;
		set<int> colorSet;
		for(int i=0;i<n;i++){
			int temp;
			cin>>temp;
			colorHouse.push_back(temp);
			colorSet.insert(temp);
		}
		int min=INT_MAX;
		for(int color : colorSet){
			int day=0;
			for(int i=0;i<colorHouse.size();){
				if(color == colorHouse[i]) {
					i++;
					continue;
				}
				day++;
				i=i+k;
			}
			if(min>day){
				min=day;
			}
		}
			cout<<min<<endl;
	}
	return 0;
}

动态规划

动态规划其实就是把一个大问题分解为一步一步的小问题,先解决这个小问题,并且保存这个小问题的答案再向后递推,并且在递推的过程中,直接读取前面的答案而不是再算一遍。
动态规划的解题五步走
1.确定dp数组的含义
2.确定dp数组的递推公式
3.确定dp数组的初始化(不要让脏数据侵入)
4.确定dp数组的递推顺序(不要让脏数据侵入)
5.如果有问题,请打印dp数组判断哪里有问题

01背包问题

问题概述:在一个洞穴里有m个物品,他们都有数量和价值,并且我们有一个有限容量的背包,题目会问我们,我们能够带走的最大价值是多少。
方法1:二维数组法
1.确定dp数组的含义
dp[i][j] 表示背包容量为j的情况下,将0-i号物品任取能够获取的最大价值。
2.确定dp数组的递推公式
当前的物品其实只有两种状态,一种是放,一种是不放,那么我们在确定这个dp[i][j]值的时候,我们只需要确定当前价值是放这个i号物品价值更高呢,还是不放更高即可。
dp[i][j] = max( dp[i-1][j], dp[i-1][j- weight(i)] +value(i)]
dp[i-1][j] 就是不放这个物品,他描述的是背包容量为j的情况下,在i-1号元素中任选能够获得的最大值。
如果放,那么这个价值就是,0-i-1号元素任选,背包容量是j-weight也就是刚刚能放下i号元素的这个背包的所能放的最大价值加上i的价值。
3.确定dp数组的初始化(不要让脏数据侵入)
首先我们看dp[0][j] 也就是第一行,他初始化就是看能否放下编号为0的物品,如果不能放下就初始化为0,能放下就初始化为这个物品的价值,再看dp[i][0]这个列全部初始化为0,为什么呢? 因为这里背包容量为0,自然能放的价值也就是0。
4.确定dp数组的递推顺序(不要让脏数据侵入)
可以看到
( dp[i-1][j], dp[i-1][j- weight(i)] +value(i))
dp[i][j] 是由他的正上方,或者上一行的左边推出的j-weight>=0 不然就越界了这里需要判断,所以我们从第二行第二列开始遍历即可,一行一行的遍历或者一列一列的遍历都可以
5.如果有问题,请打印dp数组判断哪里有问题

#include<bits/stdc++.h>
using namespace std;
typedef struct stuff{
    int weight;
 	int value;
	//构造函数
	stuff(int w,int v){
		//this是指针要用->语法 
		this->weight=w;
		this->value=v;
	}
}stuff;
int main()
{
 	int totoalWeight,N;
 	//物品数组 
 	vector <stuff> v;
 	cin>>N>>totoalWeight;
 	for(int i=0;i<N;i++){
 		int weight,value;
 		cin>>weight>>value;
 		stuff temp =stuff(weight,value);
 		v.push_back(temp);
	 }
	 //初始化dp数组,初始化第一行和第一列,(行数,每行长什么样) 
	 vector <vector <int>> dp(N,vector <int>(totoalWeight+1,0));
	 	for(int j=1;j<dp[0].size();j++){
	 		if(j>=v[0].weight)
	 		dp[0][j]=v[0].value;
	}
	//遍历,从第二行第二列开始遍历
	for(int i=1;i<N;i++){
		for(int j=1;j<dp[i].size();j++){
			//要装得下 
			if(j-v[i].weight>=0){
				dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i].weight]+v[i].value);
			}else{
				dp[i][j] = dp[i-1][j];
			}
		}
	}
	//打印dp数组 
//	for(int i=0;i<N;i++){
//		for(int j=0;j<dp[i].size();j++){
//			cout<<dp[i][j]<<" ";
//		}
//		cout<<endl;
//	}
//	
	//输出结果,这里有个小细节 j的下标要到totalWeight 
	cout<<dp[N-1][totoalWeight];
  return 0;
}

我们打印dp[物品个数-1][背包容量]就是我们需要的值。
方法2:一维数组(状态压缩)
核心:我们一定要从二维数组来理解,我们其实就是将一个矩阵,压缩为了一行代码,然后在这个代码中依次循环利用。这样说可能比较抽象,那么我们看一下二维数组的打印:
初始化:
在这里插入图片描述
在这里插入图片描述
真正需要更新的数据是红色框框圈出来的数据,从第二行,第二列开始,我们可以一行一行遍历,(先遍历物品,再遍历背包)也可以一列一列的遍历(先遍历背包,再遍历物品) 因为不管是如何,他们的上方和左上方是都没有脏数据的。

遍历完成:
在这里插入图片描述
所以根据一维dp数组就是二维dp数组的压缩来看,我们dp数组的第一次初始化就是二维数组的第一行,并且我们只能先遍历物品,再遍历背包(选中一个物品,跑所有背包)并且我们必须,从后往前遍历,为什么呢? 我们可以认为对这个dp状态的赋值就是从上一行拿了一个数据下来,如果我们从前往后遍历,我们前面的数据就会被污染,不是dp数组中上一行的数据,如果从后往前遍历,那么前面的数据是不会被污染的。 并且我们可以看到,在二维数组中也是可以从后往前遍历的(其实从前往后就是完全背包的做法)

#include<bits/stdc++.h>
using namespace std;
typedef struct stuff{
    int weight;
 	int value;
	//构造函数
	stuff(int w,int v){
		//this是指针要用->语法 
		this->weight=w;
		this->value=v;
	}
}stuff;
void pr(vector<int> dp){
	for(int i=0;i<dp.size();i++){
		cout<<dp[i]<<" ";
	}
	cout<<endl;
}
int main()
{
 	int totoalWeight,N;
 	//物品数组 
 	vector <stuff> v;
 	cin>>N>>totoalWeight;
 	for(int i=0;i<N;i++){
 		int weight,value;
 		cin>>weight>>value;
 		stuff temp =stuff(weight,value);
 		v.push_back(temp);
	 }
	 //dp[j] 表示容量为j的背包所能带走的最大价值
	vector<int> dp(totoalWeight+1,0);
	//遍历,我们要保证dp[i][j]不会影响,所以我们要把他取成非负最小也就是零 
	for(int i=0;i<N;i++){
		for(int j=totoalWeight;j>=v[i].weight;j--){
			//跑完第一个循环,其实就是可以装下的背包,全部都是第一个物品的重量了 
				dp[j]= max(dp[j],dp[j-v[i].weight]+v[i].value);
		}
	}
	cout<<dp[totoalWeight];
  return 0;
}

动态规划解决的子序列问题
一、最长递增连续子序列
1.确定dp数组的含义
dp[i] 表示 以num[i]为结尾的最长递归子序列长度
2.确定状态转移方程
两种状态
如果当前i和i-1 构成递归子序列,那么他的值就是dp[i-1]+1
如果没有构成,dp[i] =1;
3.初始化
由于本身就是一个递增子序列,所以每个值都是1,因为每个值本身都是一个递增的子序列
4.遍历顺序
从前往后遍历。
二、最长递增子序列
和最长连续递增子序列的区别就是他不再是要求连续了,那么我们只需要在状态转移方程的时候比较即可
第i项可以和前面任意一项形成递增子序列,选择一个最大的递增子序列+1 就是这个地方的dp值

堆优化的dijstar

#include<bits/stdc++.h>
using namespace std;
const int N=1100;
int n,m; 
//花费数组,点权 
int dis[N],add[N];
bool visited[N];
//vector和pair实现的邻接表 g[i] i表示起点 取出的pair first=终点 second=权值 
vector<vector<pair<int,int>>> g(N);
//堆优化的dij 
void dij(){
	for(int i=1;i<=n;i++){
		dis[i]=INT_MAX;
	}
	dis[1]=0;
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;
	//位权,起点到这个点的最短路径  起点到1这个点的最短路径为0 
	q.push({0,1});
	while(q.size()){
		int u = q.top().second;
		q.pop();
		if(!visited[u]){
			visited[u]=true;
			for(int i=0;i<g[u].size();i++){
				int cost = g[u][i].second;
				int v = g[u][i].first;
				if(dis[v]>dis[u]+cost){
					dis[v]=dis[u]+cost;
					q.push({dis[u]+cost,v});
				}
			}
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>add[i];
	}
	for(int i=0;i<m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		g[a].push_back({b,c+add[b]});
		g[b].push_back({a,c+add[a]});
	}
	dij();
	cout<<dis[n]-add[n];
	return 0;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值