计蒜客之动态规划篇(1)

版权声明:欢迎指出文章不足之处~~~ https://blog.csdn.net/zhui_xiuge/article/details/80558889

1. 爬楼梯

//用组合数的方法比较拖沓
#include <iostream>
using namespace std;
const int N = 55;

int n;
unsigned dp[N]; //dp[i]:从阶梯0跳到n的方法数, 爆int

void solve() {
    dp[1] = dp[0] = 1;
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2]; //可用快速矩阵幂
    }
    cout << dp[n] << '\n';
}

int main() {
    cin >> n;
    solve();
    return 0;
}

2. 跳跃游戏2

#include <iostream>
#include <climits>
using namespace std;
const int N = 105;
const int INF = INT_MAX;

int n;
int jump_len[N];
int dp[N]; //从下标1到i最少跳跃次数(区别跳跃长度)

void read() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> jump_len[i];
}

void solve() {
    //init
    dp[1] = 0;
    for (int i = 2; i <= n; i++) dp[i] = INF;

    for (int i = 2; i <= n; i++) {
        for (int j = 1; j < i; j++) {
            //dp[j]!=INF:可以从下标1跳到j位置
            if (dp[j] != INF && jump_len[j] >= i - j && dp[j] + 1 < dp[i]) {
                dp[i] = dp[j] + 1;
            }
        }
    }
    cout << dp[n] << '\n';
}

int main() {
    read();
    solve();
    return 0;
}

3. 等和的分隔子集

#include <iostream>
using namespace std;
const int MAX_SUM = 391; //(39+1)*39/2
const int N = 40;

int n;
//dp[i][j]:区间[1,i]内,和为j的子集个数
unsigned dp[N][MAX_SUM]; //又爆int
//unsigned dp[MAX_SUM];

void solve() {
    int sum = (n + 1)*n / 2;
    if (sum % 2) cout << "0\n"; //奇数和无法划分 
    else {
        int half_sum = sum / 2;
        for (int j = 1; j <= half_sum; j++) dp[0][j] = 0;
        //下面两条for语句可交换
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= half_sum; j++) {
                //核心
                if (i > j) dp[i][j] = dp[i - 1][j];
                else if (i == j) {
                    dp[i][j] = dp[i - 1][j] + 1;
                }
                else {
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - i];
                }
            }
        }
        //对称:取结果的一半
        cout << dp[n][half_sum] / 2 << '\n';
        /*优化
        dp[0] = 1; //init
        for (int i = 1; i <= n; i++) {
            for (int j = half_sum; j >= i; j--) {
                //逆着推,新的dp[j]表示前面不包含i和包含i的情况之和
                dp[j] += dp[j - i];
            }
        }
        cout << dp[half_sum] / 2 << '\n';
        */
    }
}

int main() {
    cin >> n;
    solve();
    return 0;
}
  1. 2018/7/11 1:09

4 程序设计:划分整数

分析: dp[n][k]表示正整数n分解为不多于k个正整数相交形式的个数,则有:

d p [ n ] [ k ] = { d p [ n ] [ n ] n < k d p [ n ] [ k 1 ] + 1 n = k d p [ n ] [ k 1 ] + d p [ n k ] [ k ] n > k

这里仅对n > k的情形做以说明:
1.n恰好分解成k个整数,此时不易直接表示,但注意到分解的k个整数皆>=1,那么我们把分解的k个整数都减去1,即知可用dp[n-k][k]表示
2.n分解为小于k个整数,为dp[n][k-1]

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int MAX_N = 305;
const int MAX_K = 305;

int n, k;
ll dp[MAX_N][MAX_K];

void solve() {
    memset(dp, 0, sizeof(dp));

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= k; j++) {
            if (j < i) dp[i][j] = dp[i][j - 1] + dp[i - j][j];
            else if (j == i) {
                dp[i][j] = dp[i][j - 1] + 1;
            }
            else dp[i][j] = dp[i][i];
        }
    }
    /*for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= k; j++) {
            if (dp[i][j] > INT64_MAX) 
                cout << "overleap!\n";
        }   
    }*/
    cout << dp[n][k] << '\n';
}

int main() {
    cin >> n >> k;
    solve();
    return 0;
}

参照:
【1】等和的分隔子集:https://blog.csdn.net/z9550695/article/details/47339895
【2】程序设计:划分整数:https://blog.csdn.net/Go_Accepted/article/details/79762494

猜你喜欢

转载自blog.csdn.net/zhui_xiuge/article/details/80558889