动态规划
走楼梯
楼梯有N级台阶,上楼可以一步上一阶,也可以一步上二阶。编一递归程序,计算共有多少种不同走法?
输入
输入N (1 <= n <=36)
输出
输出多少种走法
样例输入 3
样例输出 3
#include <bits/stdc++.h>
using namespace std;
int Upstair(int N) {
if (N == 1) {
return 1;
}
if (N == 2) {
return 2;
}
int prev2 = 1; // 对应于N==1的情况
int prev1 = 2; // 对应于N==2的情况
int current = 0;
for (int i = 3; i <= N; i++) {
current = prev1 + prev2; // 当前值是前两个值的和
prev2 = prev1; // 更新倒数第二个值
prev1 = current; // 更新倒数第一个值
}
return current;
}
int main() {
int N;
cin >> N;
cout << Upstair(N) << endl;
}
01背包问题
给定n(n<=100)种物品和一个背包。物品i的重量是wi(wi<=100),价值为vi(vi<=100),背包的容量为C(C<=1000)。
应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。
共有n+1行输入:
第一行为n值和c值,表示n件物品和背包容量c;
接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。
输出格式:
输出装入背包中物品的最大总价值。
输入样例:
在这里给出一组输入。例如:
5 10
2 6
2 3
6 5
5 4
4 6
输出样例:
15
#include <iostream>
#include <vector>
using namespace std;
int knapsack(int n, int C, vector<pair<int, int>>& items) {
// dp[i][w] 表示在前i个物品中,容量为w的背包所能装下的最大价值
vector<vector<int>> dp(n + 1, vector<int>(C + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int w = 1; w <= C; ++w) {
// 不放入第i个物品
dp[i][w] = dp[i - 1][w];
// 放入第i个物品,前提是当前背包容量能够容纳该物品
if (items[i - 1].first <= w) {
dp[i][w] = max(dp[i][w], dp[i - 1][w - items[i - 1].first] + items[i - 1].second);
}
}
}
// 返回背包能装下的最大价值
return dp[n][C];
}
int main() {
int n, C;
cin >> n >> C;
vector<pair<int, int>> items(n);
for (int i = 0; i < n; ++i) {
cin >> items[i].first >> items[i].second;
}
cout << knapsack(n, C, items) << endl;
return 0;
}
最长公共子串
输入格式:
首先输入一个整数T,表示测试数据的组数,然后是T组测试数据。每组测试数据在第一行中输入主串s,在第二行中输入子串t,s和t中不包含空格。
输出格式:
对于每组测试,输出两行,第一行是最长公共子串的长度,第二行是最长公共子串(以第一个串中字符的出现次序优先,参看输出样例)。
输入样例:
2
abcfbc
abfcab
abfcab
abcfbc
输出样例:
4
abcb
4
abfc
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 用于计算最长公共子序列的长度和内容
void LCS(const string &s, const string &t, int &length, string &lcs)
{
int m = s.size(), n = t.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// 填充dp数组
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (s[i - 1] == t[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
length = dp[m][n]; // 最长公共子序列的长度
// 通过dp数组回溯找到最长公共子序列
lcs = "";
int i = m, j = n;
while (i > 0 && j > 0)
{
if (s[i - 1] == t[j - 1])
{
lcs = s[i - 1] + lcs; // 当前字符是LCS的一部分
--i;
--j;
}
else if (dp[i - 1][j] >= dp[i][j - 1])
{ // 优先考虑第一个字符串
--i;
}b
else
{
--j;
}
}
}
int main()
{
int T;
cin >> T;
while (T--)
{
string s, t;
cin >> s >> t;
int length;
string lcs;
LCS(s, t, length, lcs);
cout << length << endl;
cout << lcs << endl;
}
return 0;
}
矩阵连乘问题
输入格式:
输入有两行。第一行一个n表示矩阵的个数;第二行有n+1个数,分别为p0,p1…pn。
输出格式:
一个数,表示最少的乘法次数。
输入样例:
6
30 35 15 5 10 20 25
输出样例:
15125
#include <bits/stdc++.h>
using namespace std;
int matrixChainOrder(vector<int> &p, int n)
{
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int L = 2; L < n; L++)
{
for (int i = 1; i < n - L + 1; i++)
{
int j = i + L - 1;
dp[i][j] = INT_MAX;
for (int k = i; k <= j - 1; k++)
{
int q = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j];
if (q < dp[i][j])
dp[i][j] = q;
}
}
}
return dp[1][n - 1];
}
int main()
{
int n;
cin >> n;
vector<int> p(n + 1);
for (int i = 0; i <= n; i++)
{
cin >> p[i];
}
cout << matrixChainOrder(p, n + 1) << endl;
system("pause");
}
状态压缩+动态规划/DFS
编写一个程序,确定哪些矮人是合法的,也就是说,从九个数字中选择七个和为100的数字。
输入格式:
共有9行输入。每个包含一个介于1和99(含)之间的整数。所有的数字都是不同的。
注意:测试数据是唯一的。
输出格式:
你的程序必须精确地产生七行输出——白雪公主七个小矮人帽子上的数字。按任意顺序输出数字。
输入样例1:
7
8
10
13
15
19
20
23
25
输出样例1:
7
8
10
13
19
20
23
#include <iostream>
#include <vector>
using namespace std;
vector<int> dwarfs(9); // 创建一个长度为9的向量,用于存储九个矮人的帽子数
vector<int> solution; // 用于存储符合条件的七个矮人的帽子数
// 使用深度优先搜索(DFS)算法寻找符合条件的七个矮人
// index: 当前考虑的矮人索引
// count: 已选矮人的数量
// sum: 已选矮人帽子数的总和
// state: 用位表示的当前已选矮人的状态,每一位代表一个矮人是否被选中
bool dfs(int index, int count, int sum, int state)
{
// 如果已选了7个矮人且它们的帽子数之和为100,则找到一个解
if (count == 7 && sum == 100)
{
// 遍历所有矮人,将符合条件的矮人的帽子数加入solution向量
for (int i = 0; i < 9; ++i)
{
if (state & (1 << i)) // 使用位操作检查矮人是否被选中
{
solution.push_back(dwarfs[i]);
}
}
return true;
}
// 如果已遍历所有矮人或者已选矮人数超过7或帽子数之和超过100,则回溯
if (index == 9 || count > 7 || sum > 100)
{
return false;
}
// 尝试选择当前矮人并递归搜索
if (dfs(index + 1, count + 1, sum + dwarfs[index], state | (1 << index)))
{
return true;
}
// 不选择当前矮人并递归搜索
if (dfs(index + 1, count, sum, state))
{
return true;
}
// 如果以上两种情况都不成功,返回false进行回溯
return false;
}
int main()
{
// 读取九个矮人的帽子数
for (int i = 0; i < 9; ++i)
{
cin >> dwarfs[i];
}
// 从第0个矮人开始搜索解决方案
dfs(0, 0, 0, 0);
// 输出找到的解决方案,即符合条件的七个矮人的帽子数
for (int num : solution)
{
cout << num << endl;
}
return 0;
}
数位DP问题
Bozˇo是个奇怪的小男孩。他每天都用奇怪的问题来烦他的朋友。今天的问题是:区间[A,B]中有多少个整数的位数之和是s,哪个是最小的?
写一个程序来回答Bozˇo的问题,这样他就可以睡一觉了。
输入格式:
输入包含三个整数A、B和S(1≤A≤B<1015,1≤S≤135).
输出格式:
第一行应该包含区间中的整数个数,其数字和等于S。
第二行应该包含这样的最小整数。
输入数据将保证第一个数字至少为1
得分
正确输出两个数字中的一个,你将获得50%的分数。
注意:如果你只想得到第二个数字的分数,一定要输出一些东西(例如0)作为第一个数字,这样判题系统才能正确解释你的输出。
输入样例1:
1 9 5
输出样例1:
1
5
输入样例2:
1 100 10
输出样例2:
9
19
输入样例3:
11111 99999 24
输出样例3:
5445
11499
#include <bits/stdc++.h>
using namespace std;
const int MAX_DIGITS = 20; // 由于范围很大,我们可能需要更多的数位
const int MAX_SUM = 165; // 数位之和的最大值,9*18(考虑到最多18位数字)
long long dp[MAX_DIGITS][2][MAX_SUM]; // DP数组:位置、限制标记、已经累积的数位和
// 将数字转换为数位数组
vector<int> toDigits(long long x)
{
vector<int> digits;
while (x)
{
digits.push_back(x % 10);
x /= 10;
}
reverse(digits.begin(), digits.end());
return digits;
}
// 计算一个数字的数位和
int digitSum(long long x)
{
int sum = 0;
while (x)
{
sum += x % 10;
x /= 10;
}
return sum;
}
long long solve(const vector<int> &digits, size_t pos, int tight, int sum)
{
if (pos == digits.size())
return sum == 0;
if (sum < 0)
return 0;
if (!tight && dp[pos][0][sum] != -1)
return dp[pos][0][sum];
if (tight && dp[pos][1][sum] != -1)
return dp[pos][1][sum];
long long ans = 0;
int limit = tight ? digits[pos] : 9;
for (int d = 0; d <= limit; ++d)
{
ans += solve(digits, pos + 1, tight && d == limit, sum - d);
}
dp[pos][tight][sum] = ans;
return ans;
}
long long countSolve(long long n, int s)
{
memset(dp, -1, sizeof(dp));
vector<int> digitsN = toDigits(n);
return solve(digitsN, 0, 1, s);
}
long long findMinNumberBinarySearch(long long a, long long b, int s)
{
long long left = a, right = b, mid, res = -1;
while (left <= right)
{
mid = left + (right - left) / 2;
long long countMid = countSolve(mid, s);
long long countA = countSolve(a - 1, s);
if (countMid - countA > 0)
{
res = mid; // Found a candidate
right = mid - 1; // Try to find a smaller number
}
else
{
left = mid + 1;
}
}
return res; // This will be the smallest number satisfying the condition
}
int main()
{
long long a, b;
int s;
cin >> a >> b >> s;
memset(dp, -1, sizeof(dp)); // 初始化DP数组
vector<int> digitsB = toDigits(b);
long long countB = solve(digitsB, 0, 1, s);
memset(dp, -1, sizeof(dp)); // 重置DP数组
vector<int> digitsA = toDigits(a - 1);
long long countA = solve(digitsA, 0, 1, s);
cout << countB - countA << endl; // 输出满足条件的数字数量
long long minNumber = findMinNumberBinarySearch(a, b, s);
cout << minNumber << endl;
return 0;
}