2022-02-21每日刷题打卡
一本通——动态规划
1281:最长上升子序列
【题目描述】
一个数的序列bibi,当b1<b2<…<bSb1<b2<…<bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1,a2,…,aN)(a1,a2,…,aN),我们可以得到一些上升的子序列(ai1,ai2,…,aiK)(ai1,ai2,…,aiK),这里1≤i1<i2<…<iK≤N1≤i1<i2<…<iK≤N。比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。这些子序列中最长的长度是4,比如子序列(1,3,5,8)。
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
【输入】
输入的第一行是序列的长度N(1≤N≤1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
【输出】
最长上升子序列的长度。
【输入样例】
7 1 7 3 5 9 4 8
【输出样例】
4
准备一个动态规划数组dp,dp[i]的意思是,以第i个数为末尾的最长递增序列为dp[i]。遍历数组,每次遍历一个位置便从那个位置回头遍历一遍,如果回头遍历的数小于当前的数,就把那个数的dp+1赋给当前数的dp(此过程中要取最大值)。
最后遍历一遍dp数组,取最大值输出。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string.h>
#include<string>
#include<unordered_map>
#include<math.h>
#include<queue>
int main()
{
int n;
cin >> n;
vector<int>v(n);
for (int i = 0; i < n; i++)cin >> v[i];
vector<int>dp(n,1);
int res = 1;
for (int i = 1; i < n; i++)
{
for (int j = i-1; j >= 0; j--)
{
if (v[i] > v[j])
{
dp[i] = max(dp[i], dp[j] + 1);
res = max(res, dp[i]);
}
}
}
cout << res << endl;
return 0;
}
【例9.11】01背包问题
【题目描述】
一个旅行者有一个最多能装 MM 公斤的背包,现在有 nn 件物品,它们的重量分别是W1,W2,…,WnW1,W2,…,Wn,它们的价值分别为C1,C2,…,CnC1,C2,…,Cn,求旅行者能获得最大总价值。
【输入】
第一行:两个整数,MM(背包容量,M<=200M<=200)和NN(物品数量,N<=30N<=30);
第2…N+12…N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4 2 1 3 3 4 5 7 9
【输出样例】
12
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string.h>
#include<string>
#include<unordered_map>
#include<math.h>
#include<queue>
typedef long long ll;
const int N = 210;
ll f[N], v[N], w[N];
int main()
{
int n,m;
cin >> n >> m;
for (int i = 0; i < m; i++)
cin >> v[i] >> w[i];
for (int i = 0; i < m; i++)
{
for (int j = n; j >= v[i]; j--)
{
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
cout << f[n] << endl;
return 0;
}
1259:【例9.3】求最长不下降序列
【题目描述】
设有由n(1≤n≤200)n(1≤n≤200)个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)b(1)、b(2)、……、b(n)若存在i1<i2<i3<…<iei1<i2<i3<…<ie 且有b(i1)<=b(i2)<=…<=b(ie)b(i1)<=b(i2)<=…<=b(ie)则称为长度为e的不下降序列。程序要求,当原数列出之后,求出最长的不下降序列。
例如13,7,9,16,38,24,37,18,44,19,21,22,63,15
。例中13,16,18,19,21,22,63
就是一个长度为77的不下降序列,同时也有7 ,9,16,18,19,21,22,63
组成的长度为88的不下降序列。
【输入】
第一行为nn,第二行为用空格隔开的nn个整数。
【输出】
第一行为输出最大个数maxmax(形式见样例);
第二行为maxmax个整数形成的不下降序列,答案可能不唯一,输出一种就可以了,本题进行特殊评测。
【输入样例】
14
13 7 9 16 38 24 37 18 44 19 21 22 63 15
【输出样例】
max=8
7 9 16 18 19 21 22 63
注意这个题,说的是不递减,不是递增,意思是等于和大于都可以,就是这里我被卡了一天,但题目明明说了数都各不相等,哪还有等于的说法嘛,出题不仔细啊,知道是这个地方出问题的时候我都要疯了(哭
就是普通的求个最长不递减序列的长度问题(做法同求最长递增序列),但是多了个输出序列的部分,对于这点有两个方法。
一:就先正常的求最长不递减序列的长度,然后记下最长序列的末尾数下标ans。然后根据这个下标为起点回头找,要找的是数小于当前元素,且长度比最长长度小1的元素,找到后更新一下记录的最长长度和元素,继续找下一个长度比记录长度小1且数小于我们记录的数的……以此类推,当找到最长长度为1时结束,把找到的序列输出。例:比如按照样例来说,最长长度是8,序列的末尾元素是63,下标是12,我们以12为起点去找长度为7的序列的末位数,且要小于63,即22,再去找长度为6……。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string.h>
#include<string>
#include<unordered_map>
#include<math.h>
#include<queue>
const int N = 210;
int f[N],h[N];
void printf01(int num)
{
cout << num << " ";
}
int main()
{
int n;
cin >> n;
unordered_map<int, vector<int>>mymap;
for (int i = 0; i < n; i++)
{
cin >> h[i];
}
f[0] = 1;
int res = 1,ans=0;
for (int i = 1; i < n; i++)
{
f[i] = 1;
for (int j = i - 1; j >= 0; j--)
{
if (h[i] >= h[j])
{
f[i] = max(f[i], f[j] + 1);
if (f[i] > res)
{
res = f[i];
ans = i;
}
}
}
}
cout << "max=" << res << endl;
vector<int>v;
int num = h[ans];
v.push_back(num);
for (int i = ans - 1; i >= 0; i--)
{
if (f[i] == res - 1 && h[i] <= num)
{
res--;
num = h[i];
v.push_back(num);
}
}
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), printf01);
return 0;
}
第二个方法是用一个数组p,记录序列的上一个元素的下标,比如我们求样例的最长长度时,遍历到第3个元素9时,我们知道它可以接到第二个元素7上,所以数组里p[2]=1;如果当前遍历到的元素不能接到任何数上,那数组p就记录自身的下标,比如p[0]=0。这样依次类推,我们求完最长长度后,记录序列最后一个元素的下标,根据这个下标在p数组中取出序列的上一个下标……以此类推,当取出的下标为自身时,说明这是序列的最后一个数,结束程序。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string.h>
#include<string>
#include<unordered_map>
#include<math.h>
#include<queue>
typedef long long ll;
const int N = 210;
ll f[N], h[N], p[N];
void printf01(ll num)
{
cout << num << " ";
}
int main()
{
int n;
cin >> n;
unordered_map<ll, vector<ll>>mymap;
for (int i = 0; i < n; i++)
{
cin >> h[i];
p[i] = i;
}
f[0] = 1;
ll res = 1, ans = 0;
//13 7 9 16 38 24 37 18 44 19 21 22 63 15
for (int i = 1; i < n; i++)
{
f[i] = 1;
for (int j = i - 1; j >= 0; j--)
{
if (h[i] >= h[j])
{
if (f[j] + 1 > f[i])
{
f[i] = f[j]+1;
p[i] = j;
}
if (f[i] > res)
{
res = f[i];
ans = i;
}
}
}
}
cout << "max=" << res << endl;
vector<ll>v;
bool flag = true;
while (ans >= 0 && (p[ans] != ans) || (p[ans] == ans && flag))
{
if (p[ans] == ans)flag = false;
v.push_back(h[ans]);
ans = p[ans];
}
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), printf01);
return 0;
}
蓝桥杯——历届真题
历届真题 特别数的和【第十届】【省赛】【B组】
问题描述
小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 0),在 1 到
40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574。请问,在 1 到 n 中,所有这样的数的和是多少?
输入格式
输入一行包含两个整数 n。
输出格式
输出一行,包含一个整数,表示满足条件的数的和。
样例输入
40
样例输出
574
评测用例规模与约定
对于 20% 的评测用例,1 ≤ n ≤ 10。 对于 50% 的评测用例,1 ≤ n ≤ 100。对于 80% 的评测用例,1 ≤ n ≤ 1000。对于所有评测用例,1 ≤ n ≤ 10000。
#include<iostream>
using namespace std;
int main()
{
int n,res=0;
cin >> n;
for (int j = 1; j <= n; j++)
{
int num = j;
while (num)
{
int ans = num % 10;
num /= 10;
if (ans == 0 || ans == 1 || ans == 2 || ans == 9)
{
res += j;
break;
}
}
}
cout << res << endl;
return 0;
}