枚举
小蓝的漆房
分析:由于题目最后的结果是相同的颜色,所以我们可以去寻找,把所有房子刷成这个颜色所需要的时间,然后在这里面找一个最小值
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;
}