动态规划

来源:互联网 发布:ubuntu syslog函数 编辑:程序博客网 时间:2024/05/20 20:03

1. split_1

问题描述:将一个数组v分成a,b两组,设最小难度为min(a中相邻元素之差绝对值和+b中相邻元素之差绝对值和)

输入描述:输入n(2 <= n <=2000),表示数组长度,接下来输入n个数。

5

1 5 6 2 1

输出描述:

3

ps:分成1 2 1和5 6两组时,最小难度为3=2+1

[分析]:

状态描述:dp[i][j]表示第i个元素分给a,第j个元素分给b时的最小难度和。

状态转移方程:要结合递归去表述,dp[la][lb] = min(solve(now, lb) + (la ? abs(v[now] - v[la]) : 0), solve(la, now) + (lb ? abs(v[now] - v[lb]) : 0)),now=max(la, lb)+1.

时间复杂度分析:实际上搜索过程就是一个满二叉树,高度为n,时间复杂度为2^n,因此在搜索过程中,用到了记忆搜索,整个过程会小于计算的最大时间复杂度。

#include <cstdio>#include <cstdlib>#include <iostream>using namespace std;#define MAX(a, b) ((a) > (b)? (a): (b))#define MIN(a, b) ((a) < (b)? (a): (b))const int maxn = 2e3 + 5;int n, v[maxn], dp[maxn][maxn];int solve(int la, int lb) {    int now = MAX(la, lb) + 1;    if(now == n + 1) return 0;//end    if(dp[la][lb] != -1) return dp[la][lb];//已经求出此状态的最优解    return dp[la][lb] = MIN(solve(now, lb) + (la ? abs(v[now] - v[la]) : 0), solve(la, now) + (lb ? abs(v[now] - v[lb]) : 0)); //下一个元素,给a或者给b}int main() {    while(~scanf("%d", &n)) {v[0] = -1;for(int i = 1; i <= n; i++) scanf("%d", &v[i]);memset(dp, -1, sizeof(dp));printf("%d\n", solve(0, 0));}    return 0;}

2. split_2

问题描述:在第一题基础上,让两个数组的和的差绝对值最小。而且还要求出两个数组。

输入输出:同上

[分析]:严格上讲,不能算作dp(希望有能有更好的解法),而是递归求解。把每一个可能的情况,搜索一遍,找出最优解。

#include <cstdio>#include <cstdlib>#include <iostream>using namespace std;#define MAX(a, b) ((a) > (b)? (a): (b))#define MIN(a, b) ((a) < (b)? (a): (b))const int MAXN = 0x3f3f3f3f;//INT_MAX; const int maxn = 1e2 + 5;int n, v[maxn], dp[maxn][maxn], min_ans;int solve(int a[], int label[], int lb, int lc, int sumb, int sumc) {    int now = MAX(lb, lc) + 1;    if(now == n + 1) return abs(sumb - sumc);    //if(dp[lb][lc] != -1) return dp[lb][lc];//完全搜索,求出最优值。    int res_b = solve(a, label, now, lc, sumb + a[now], sumc);    int res_c = solve(a, label, lb, now, sumb, sumc + a[now]);// + abs(sumb - sumc - a[now]); is multi    //cout << now << lb << lc << " " << " " << res_b << " " << res_c << endl;    if (res_b < res_c) {        if (res_b <= min_ans) {            label[now] = 0;            min_ans = res_b;        }        return res_b;    } else {        if (res_c <= min_ans) {            label[now] = 1;            min_ans = res_c;        }                return res_c;    }}int splitToMin(int a[], int b[], int c[]) {    memset(dp, -1, sizeof(dp));    int* label = (int*)malloc(sizeof(int) * (n + 1));    min_ans = MAXN;    int ans = solve(a, label, 0, 0, 0, 0);        int lb = 0, lc = 0;    for (int i = 1; i <= n; ++i) {        cout << label[i] << " " << a[i] << endl;        if (label[i] == 0) b[lb++] = a[i];        else c[lc++] = a[i];    }    free(label);    return ans;}int main() {    while(~scanf("%d", &n)) {        v[0] = -1;        for(int i = 1; i <= n; ++i) scanf("%d", &v[i]);        int* b = (int*)malloc(sizeof(int) * (n + 1));        int* c = (int*)malloc(sizeof(int) * (n + 1));                printf("%d\n", splitToMin(v, b, c));        free(b);        free(c);    }        return 0;}

3. 构造回文串

问题描述:给定一个数组,长度为n(2<= n <= 1000),插入一些数字,使得该序列为回文串。

输入描述:输入n,接下来输出n个数字

5

1 2 3 1 2

输出描述:

11

[分析]这道题与第一题类似,也是递归求解,即,每个状态都要考虑是添加左边还是添加右边的数字。同时,这里用到了“记忆化搜索”,不然就会超时。递归公式:

dp[l, r] = MIN(solve(l, r - 1) + a[r], solve(l + 1, r) + a[l])



原创粉丝点击