动态规划题集2

杭电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 
#include 
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  //memset的头文件,之前竟然没用过
#include 
#include 
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 
#include 
#include 
#include 
#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;
}

你可能感兴趣的:(算法练习)