2022.02.17递推
1.划分数组
【题目描述】
给定一个长度为n的数列{ai},要求划分最少的段数,使得每一段要么单调不降,要么单调不升。
【输入格式】
第一行一个整数n。
第二行n个整数表示数列{ai}。
【输出格式】
一行一个整数,表示最少的划分数。
【样例】
输入:
1 2 3 2 2 1
输出:
【思路】分别以last_max和last_min表示以i结尾的最长单调序列的起点的前一位,dp[i]表示a1~ai的最少划分段数
得递推式:dp[i]=min{dp[last_min(i)],dp[last_max(i)]}+1
【代码】
#includeusing namespace std; int a[100001], last_min[100001], last_max[100001], dp[100001]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = 1; i <= n; ++i) { last_max[i] = i - 1; if (a[i] >= a[i - 1]) { last_max[i] = last_max[i - 1];//递推式 } last_min[i] = i - 1; if (a[i] <= a[i - 1]) { last_min[i] = last_min[i - 1];//递推式 } dp[i] = min(dp[last_min[i]], dp[last_max[i]]) + 1;//dp递推式 } printf("%d", dp[n]); return 0; }
2.求 f 函数
【题目描述】
给出一个函数:f(x)=f(f(x+11)) (x<=100)
x-10(x>=101)
对于给定的x,求f(x)。
【输入格式】
输入包括若干行,每行一个正整数x,最后一行以0结束。
注意0不是要求的x,只是一个结束标记。
【输出格式】
对于每一个x,输出一行一个整数表示f(x)。
【样例】
输入:
100
101
输出:
91
91
【思路】
对于大于等于101的数容易求得答案,对于100以下的数因为出现了f(f(x+11))的结构则f(89)=f(100),所以对于100以下的数结果可以通过递推求得。
又已知递推中最大值为100+11=111,求完101到111后从大到小求100以内即可
【代码】
#include
using namespace std;
int main() {
int f[112];
for (int i = 101; i <= 111; i++) {
f[i] = i - 10;
}
for (int i = 100; i >= 1; i--) {
f[i] = f[f[i + 11]];
}
int q;
while (scanf("%d", &q) && (q != 0)) {
if (q >= 101) {
printf("%d\n", q - 10);
} else {
printf("%d\n", f[q]);
}
}
return 0;
}
3.无限序列
【题目描述】
我们按以下方式产生序列:
1.初始时序列为1;
2.每一次变化都会将序列中的1变成10,0变成1。
经过无数次变化,我们得到序列1011010110110101101...
给出Q个询问,每次询问为:在区间[a,b]中有多少个1。
【输入格式】
第一行一个整数Q。
接下来Q行,每行两个整数a,b。
【输出格式】
对于每个询问,输出一行一个整数表示答案。
【样例】
输入:
2 8
输出:
【思路】
设经过i此变化得到序列为f(i),f(i)=f(i-1)+f(i-2),则第i个序列可以拆分成第i-1个序列和第i-2个序列。可以用递归处理。
注:序列变化的长度与其中包含1的数量分别为开头为1 2和1 1的斐波那契数列
【代码】
#include#define ll long long using namespace std; ll sizef[91], num1[91]; inline ll solve(ll v) { if (v == 0) return 0; int Max = 0; for (int i = 90; i >= 1; i--) { if (sizef[i] <= v) { Max = i; break; } } return num1[Max] + solve(v - sizef[Max]); } int main() { sizef[1] = 1; sizef[2] = 2; num1[1] = 1; num1[2] = 1; for (int i = 3; i <= 90; i++) { sizef[i] = sizef[i - 1] + sizef[i - 2]; num1[i] = num1[i - 1] + num1[i - 2]; } int q; scanf("%d", &q); ll a, b; for (int i = 1; i <= q; i++) { scanf("%lld%lld", &a, &b); printf("%lld\n", solve(b) - solve(a - 1)); } return 0; }
4.序列个数
【题目描述】
一个由1~n这n个数字组成的序列对于任意一个数i,要求满足b1,b2,...,bi中,小于等于i的数的个数恰好为ai的序列一共有多少个。
【输入格式】
第一行一个整数n。
接下来n个数,表示a1,a2,...,an。
【输出格式】
一行一个数,表示满足条件的排列数。
由于答案很大,只要求输出答案mod340610的结果。
【样例】
输入:
0 1 3
输出:
【思路】
可以将构造序列的过程转化为构造矩阵的过程,即在n*n的矩阵中放入n个1,要求每行每列只有一个1。
例:23415=0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
1 0 0 0 0
0 0 0 0 1
所以ai可以看作以(1,1)为左上角、以(i,i)为右下角的矩阵中1的个数,a(i)-a(i-1)表示的是一个L形(左右颠倒)中1的个数。
然后就可以从(1,1)一层一层展开用乘法原理统计可能性
【代码】
#includeusing namespace std; const int mod = 340610; int main() { int n; long long ans = 1; scanf("%d", &n); int a[n + 1]; a[0] = 0; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = 1; i <= n; i++) { if (a[i] - a[i - 1] == 1) { ans *= (i * 2 - 1) - 2 * a[i - 1]; } if (a[i] - a[i - 1] == 2) { ans *= ((i - 1) - a[i - 1]) * ((i - 1) - a[i - 1]); } if (a[i] - a[i - 1] >= 3) { ans = 0; } ans %= mod; } printf("%lld\n", ans); return 0; }
5.平铺方案
【题目描述】
试问可以通过几种方式用2*1或2*2的瓦片平铺2*n的矩形。
【输入格式】
输入包括若干行。
每行一个整数n。
【输出格式】
对于每个n,输出一行一个整数,表示方案数。
【样例】
输入:
12
100
200
输出:
171
2731
845100400152152934331135470251
1071292029505993517027974728227441735014801995855195223534251
【思路】
设平铺2*i的矩形方案数为f(i),在第i列竖着摆1*2方案数为f(i-1),在第i与i-1列摆2*2方案数为f(i-2),在第i与i-1列横摆2个2*1方案数为f(i-2)。
总递推式:f(i)=f(i-1)+f(i-2)+f(i-2)
注:小知识:简单加法比乘法运算速度快。
注:本题数据范围丧心病狂,需要用到高精度结构体。
【代码】
#includeusing namespace std; struct BigInt { static const int M = 1000; int num[M + 10], len; BigInt() { clean(); } void clean() { memset(num, 0, sizeof(num)); len = 1; } void write() { for (int i = len; i >= 1; i--) { printf("%d", num[i]); } printf("\n"); } void itoBig(int x) { clean(); while (x != 0) { num[len++] = x % 10; x /= 10; } if (len != 1) len--; } BigInt operator+(const BigInt &A) const { BigInt S; S.len = max(len, A.len); for (int i = 1; i <= S.len; i++) { S.num[i] += num[i] + A.num[i]; if (S.num[i] >= 10) { S.num[i] -= 10; S.num[i + 1]++; } } while (S.num[S.len + 1]) S.len++; return S; } } f[251]; int main() { f[0].itoBig(1), f[1].itoBig(1), f[2].itoBig(3); for (int i = 3; i <= 250; i++) { f[i] = f[i - 2] + f[i - 2] + f[i - 1]; } int q; while (scanf("%d", &q) != (EOF)) { f[q].write(); } return 0; }