算法一:函数,递归及递推练习题解

P1028 —— 数的计算

使用递推进行考虑,每个数只能连接这个数的一半及一下,则考虑递推求解,例如,开头是6,长度为3,由题意,及从1到3循环考虑开头为1,2,3长度为2的种类个数,而开头为1,2,3长度为2的个数又由0,1,1为开头,长度为1推得,所以得出递推式子:

        dp[i][j] = \sum_{k = 1}^{i / 2}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] = \sum_{j = 1}^{k}(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();
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值