动态规划题集2

来源:互联网 发布:快手视频制作软件 编辑:程序博客网 时间:2024/06/17 21:38

杭电1171

原题链接: http://acm.hdu.edu.cn/showproblem.php?pid=1171
大意是: 给定n个物品的价值,还有对应的数量。然后将这些物品分成A, B两部分,使每部分的总价值尽可能的相等,且A的价值不小于B。
开始我想的将它转成0-1背包问题。求B的最大价值,它的背包容量为总价值/2。
递推式是

d[i][j]=maxd[i1][j],d[i1][jvalue(i)]+value[i]

这样,写出代码来,不过运行超时超空间,从网上看到要用母函数来做。

下面是用动态规划做的,样例都已通过,可是超时超空间:再下面是重新写的代码。
这个代码错误的原因是:数组范围开辟有问题,最大的价值,即背包容量应该是50*50*100,所以只能用一维数组来解决0-1背包,代码在后面。
递推式是

d[i]=max|d[ivalue[j]]+value[j]|

#include <iostream>#include <algorithm>using namespace std;int value[5005] = { 0 };int d[5005][2505] = { 0 };int main(){    int n;    while (true){        int flag = 0;        int sum = 0;        int num = 0;        cin >> n;        if (n < 0) break;        int v, m;        for (int i = 0; i < n; i++){            cin >> v >> m;            for (int j = 0; j < m; j++){                value[flag++] = v;            }            sum += v*m;//sum是背包容积,即总价值            num += m;//num是物品的个数        }        for (int i = 0; i <= sum / 2; i++){            if (value[0] <= i)                d[0][i] = value[0];        }        for (int i = 1; i < num; i++){            for (int j = 1; j <= sum / 2; j++){                if (j - value[i] >= 0)                    d[i][j] = max(d[i - 1][j], d[i - 1][j - value[i]] + value[i]);                else                    d[i][j] = d[i - 1][j];            }        }        cout << sum-d[num-1][sum/2] << " " << d[num-1][sum/2] << endl;    }    return 0;}

下面是更新代码,用一维数组解决0-1背包,已AC:

//#include "stdafx.h"#include <cstring> //memset的头文件,之前竟然没用过#include <iostream>#include <algorithm>using namespace std;int value[5005] = { 0 };int d[250005] = { 0 };int main(){    int n;    while (true) {        int flag = 0;        int sum = 0;        int num = 0;        cin >> n;        if (n < 0) break;        int v, m;        for (int i = 0; i < n; i++) {            cin >> v >> m;            for (int j = 0; j < m; j++) {                value[flag++] = v;            }            sum += v*m;//sum是背包容积,即总价值            num += m;//num是物品的个数        }        memset(d, 0, sizeof(d));        for (int i = 0; i <= sum/2; i++) {            for (int j = 0; j < num; j++) {                if(i-value[j]>=0)                    d[i] = max(d[i], d[i - value[j]] + value[j]);            }        }        cout << sum - d[sum/2] << " " << d[sum / 2] << endl;    }    return 0;}

杭电1176 免费馅饼

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1176
大意是:有0-10这11个点,馅饼在不同的时间落在这些点上。但是有个人初始位置在5,每秒只能移动一个单位, 求它最多能拿到多少馅饼。
这题是和数字三角形差不多的题,就是要从最后开始记录,每次记录都是和下一次的时间点的位置有关。
设a[i][j]表示第j秒有馅饼落在i位置
d[i][j]表示第i个位置时,第j秒取得的最大馅饼数量。
d[i][j] = max( d[i - 1][j + 1], d[i][j + 1] , d[i+1][j+1] )+ a[i][j] ;
递推式为上式,注意处理边界条件即可。代码如下:

#include <iostream>#include <algorithm>#include <cstdlib>#include <cstring>#include "stdio.h"using namespace std;int a[11][100005] = { 0 };//a[i][j]表示第j秒有一个馅饼落在第i个地方int d[11][100005]; //其实如果空间不足,用一个数组也行int main(){    int n;    while (true){        memset(a, 0, sizeof(a));        scanf("%d", &n);        if (n == 0) break;        int x, t, maxt = 0;        for (int i = 0; i < n; i++){            scanf("%d%d", &x, &t);            a[x][t]++;            maxt = max(maxt, t);//找出最大的t来,便于遍历数组        }        for (int i = 0; i<11; i++)            d[i][maxt] = a[i][maxt];        for (int j = maxt - 1; j >= 0; j--){            d[10][j] = max(d[9][j + 1], d[10][j + 1]) + a[10][j];            d[0][j] = max(d[1][j + 1], d[0][j + 1]) + a[0][j];            for (int i = 1; i < 10; i++){                d[i][j] = max(d[i - 1][j + 1], d[i][j + 1]);                d[i][j] = max(d[i][j],d[i+1][j+1]) + a[i][j];            }        }        printf("%d\n", d[5][0]);    }    return 0;}
原创粉丝点击