P1028 —— 数的计算
使用递推进行考虑,每个数只能连接这个数的一半及一下,则考虑递推求解,例如,开头是6,长度为3,由题意,及从1到3循环考虑开头为1,2,3长度为2的种类个数,而开头为1,2,3长度为2的个数又由0,1,1为开头,长度为1推得,所以得出递推式子:
dp[i][j] = dp[k][j - 1];
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n
void solve() {
ll ans = 0;
cin >> n;
vector<vector<ll>>dp(n + 1,vector<ll>(n + 1, 0));
for (int i = 1; i <= n; i++) {
dp[i][1] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= n; j++) {
for (int k = 1; k <= i / 2; k++) {
dp[i][j] += dp[k][j - 1];
}
}
}
for (int i = 1; i <= n; i++) {
ans += dp[n][i];
}
cout << ans;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int _ = 1; //cin >> _;
while (_--)solve();
}
P1036 ——选数
找出n个数中任意k个想加为质数的数的个数,由此可以首先想到由欧拉筛法可以节省算法运行时间,考虑到n只有20个,用dfs+不降原则(加一变量start,选择了这个数后,就从i + 1 选后面的数,避免了重复),满足质数答案则加一。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int n, k, ans;
int vis[N], prim[N], cnt;
int a[99],viss[99];
void dfs(int x, int sum,int start) {
if (x > k) {
if (!vis[sum]) {
//cout << sum << '\n';
ans++;
}
return;
}
for (int i = start; i <= n; i++) {
dfs(x + 1, sum + a[i],i + 1);
}
}
void euler(int N) {
for (int i = 2; i <= N; i++) {
if (!vis[i]) prim[++cnt] = i;
for (int j = 1; 1ll * i * prim[j] <= N; j++) {
vis[i * prim[j]] = 1;
if (i % prim[j] == 0) break;
}
}
}
void solve() {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
euler(1e7);
dfs(1, 0, 1);
cout << ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _ = 1; //cin >> _;
while (_--) solve();
}
P1464 Function
由题意模拟即可,递归时同时记忆化搜索(即同时将返回值存入数组),注意数据范围,需用longlong才能存下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll s[50][50][50];
ll w(ll a,ll b,ll c){
if(a <= 0 || b <= 0 || c <= 0)return 1;
if(a > 20 || b > 20 || c > 20)return s[20][20][20] = w(20,20,20);
if(a <b && b <c){
if(s[a][b][c])return s[a][b][c];
else s[a][b][c] = w(a,b,c - 1) + w(a,b - 1, c - 1) -w(a,b -1,c);
return s[a][b][c];
}
else {
if(s[a][b][c])return s[a][b][c];
else s[a][b][c] = w(a - 1, b, c)+w(a - 1, b - 1, c)
+w(a - 1, b, c - 1)-w(a - 1,b - 1,c - 1);
return s[a][b][c];
}
}
void solve(){
ll a,b,c;
s[15][15][15] = 32768;
while(1){
cin >> a >> b >> c;
if(a == -1 && b == -1 && c == -1)return;
printf("w(%lld, %lld, %lld) = %lld\n",a,b,c,w(a,b,c));
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;//cin >> _;
while(_--)solve();
return 0;
}
P5534 ——【XR-3】等差数列
给出公差d与项数n,使用求和公式
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,n;
void solve(){
cin >> a >> b >> n;
int d = b - a;
cout << (n*((n - 1) * d + 2 * a) ) / 2;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;//cin >> _;
while(_--)solve();
return 0;
}
P1192 台阶问题
由题意进行打表:
k=2 : 1 2 3 5 8 13 21 34...
k=3 : 1 2 4 7 13 24 44 81...
k=4 : 1 2 4 8 15 29 56 108...
k=5 : 1 2 4 8 16 31 61 120...
可以发现k=2时为斐波那契数列,观察其余式子可得出递推式:
dp[i] = (dp[i] + dp[i - j]);
即到第i个台阶的可以从第i - 1,i - 2 ...i - k(i >= k)个台阶上登陆。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 100003;
ll dp[1000010];
int n, k ;
void solve(){
dp[0] = 1;
cin >> n >> k;
for(int i = 1 ; i <= n ; i ++){
for(int j = 1 ; j <= k ; j ++){
if(i >= j)
dp[i] = (dp[i] + dp[i - j]) % M;
}
}
cout << dp[n] << '\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;//cin >> _;
while(_--)solve();
}
时间复杂度o(nk);
然而本题可以进一步推导公式将时间复杂度优化至o(n)。
当x >= k 时,由a[x] = a[x - 1] + ... + a[x - k];
与 a[x + 1] = a[x] + ... + a[x + 1 - k];
得到a[x + 1] = 2 * a[x] - a[x - k];
当x < k 时 a[x] = 2 * a[x - 1];
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 100003;
ll dp[1000010];
int n, k ;
void solve(){
dp[0] = dp[1] = 1;
cin >> n >> k;
for(int i = 2 ; i <= n ; i ++){
if(i < k)dp[i] = 2 * dp[i - 1] % M;
else dp[i] = ( 2 * dp[i - 1] - dp[i - k - 1] + M) % M;
}
cout << dp[n] << '\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;//cin >> _;
while(_--)solve();
}
P1025 [NOIP2001 提高组] 数的划分
由题意,数分为k段有两种互斥情况:
一,至少有一个1,方案数为dp[i - 1][j - 1]即第j个数字必为1。
二,一个1都没有,方案数为dp[i - k][j]用i-k填j个空和k个数填j个空即每个空都保证了>=2。
可得出递推式dp[i][j] = dp[i - k][j] + dp[i - 1][k - 1];
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k;
ll dp[220][10];
void solve(){
dp[0][0] = 1;
cin >> n >> k;
for(int i = 1 ; i <= n ; i ++){
for(int j = 1; j <= k ; j ++){
if(i >= j)
dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
}
}
cout << dp[n][k] <<'\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _= 1;//cin >> _;
while(_--)solve();
return 0;
}
当然最容易想到的还是dfs,但是需要剪枝优化(使用同上文的不降原则,实现了去重剪枝,再对选数进行优化,确认边界进一步降低时间复杂度)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k,ans;
void dfs(int x,ll sum,int start){
if(x > k){
if(sum == n)ans++;
return;
}
for(int i = start ; i <= n - sum ; i ++){
dfs(x + 1,sum + i, i);
}
}
void solve(){
cin >> n >> k;
dfs(1,0,1);
cout << ans << '\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;//cin >> _;
while(_--)solve();
}
P4994 终于结束的起点
使用斐波那契递推函数,注意递推的过程进行记忆化。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
ll f[N];
int m;
ll fib(ll x){
if(f[x])return f[x];
if(x == 1 || x == 2)return f[x] = 1;
return f[x] = (fib(x - 1) + fib(x- 2)) % m;
}
void solve(){
cin >> m;
int i= 1;
while(fib(i) != 0 || fib(i + 1) != 1){
i++;
}
cout << i <<'\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;//cin >> _;
while(_--)solve();
}