洛谷p1060——开心的金明

题目链接

题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的NN元。于是,他把每件物品规定了一个重要度,分为55等:用整数1-51−5表示,第55等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过NN元(可以等于NN元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第jj件物品的价格为v_[j]v
[ j],重要度为w_[j]w [j],共选中了kk件物品,编号依次为j_1,j_2,…,j_kj 1 ,j 2 ,…,j k ,则所求总和为:
v_[j_1] \times w_[j_1]+v_[j_2] \times w_[j_2]+ …+v_[j_k] \times w_[j_k]v [j 1]×w [ j 1 ]+v [j 2 ]×w j2 ]+…+v [j k​]×w [j k]。

请你帮助金明设计一个满足要求的购物单。

输入输出格式
输入格式:
第一行,为22个正整数,用一个空格隔开:N mNm(其中N(<30000)N(<30000)表示总钱数,m(<25)m(<25)为希望购买物品的个数。)

从第22行到第m+1m+1行,第jj行给出了编号为j-1j−1的物品的基本数据,每行有22个非负整数 v pvp(其中vv表示该物品的价格(v \le 10000)(v≤10000),pp表示该物品的重要度(1-51−5)

输出格式:
11个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)(<100000000)。

输入输出样例
输入样例#1:
1000 5
800 2
400 5
300 5
400 3
200 2
输出样例#1:
3900

01背包问题,动态规划:
其实这里就是换了一个方法来暴力地解决这个问题,怎么个暴力法呢
数组dp[i][j]表示当你面对 i 件物品,手头有j 元时的最大效益,我们可以看到,数组dp的规模是 m*n 也就是说我们暴力地枚举了所有的最优解,且前面的所有最优解都是为求得 dp[m][n]做贡献
这是再用空间换时间(当然这里给的代码是可以在空间上优化的,这里的代码是最初级的)。

好好分析下:
dp[i][j]数组的定义都差不多,表示第i件物品手里有j元时可以获得的最大效益即 重要程度*价格。
当你处于dp[i][j]时,我们来想一想,你已经在 i-1件物品里选过了,即你已经在 i-1 件物品得到了 当只有 i-1 件物品时的最大效益 。现在你能在 i 件物品中选了,手里有 j元钱,你当然是先把之前的最大效益先拿过来保存着(即dp[i-1][j],当你有j元在i-1件物品中选时),因为那是面对i-1件物品时最优解啊,为什么不用呢,当然可以用啦✪ ω ✪。拿过来后你开始观察眼前的第 i 件物品,选不选呢
1:选,那么你就得为它腾出钱腾出good[i].v的钱,于是乎,如果你选了这件物品,那么你手里就只有 j-good[i].v 的钱去买 i-1 件物品 而且你已经将它的最大效益保存在 dp[i-1][j-good[i].v]里面了呢,所以现在你买了第i件物品后你的最大效益应该是dp[i-1][j-good[i].v]+good[i].w,并把它保存在dp[i][j]中;

2:不买第i件物品,则你的最大效益是dp[i-1][j]
所以买不买就看上面两种情况哪一个效益高了,因而

dp[i][j] = max(dp[i][j], dp[i-1][j - good[i].v] + good[i].w);

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include 
//#include
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#define INF 0x7fffffff
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define sf2d(x,y) scanf("%d %d",&(x),&(y))
#define sfd(x) scanf("%d",&x)
#define sff(p) scanf("%lf",&p)
#define pfd(x) printf("%d\n",x)
#define mset(x,b) memset((x),b,sizeof(x))
const double pi = acos(-1.0);
const long long mod = 1000000007LL;

struct Node {
    int v;//价格
    int w;//重要程度

};

int dp[26][30000];

int main(void) {
    int n, m;
    Node good[26];

    while (sf2d(n, m) != EOF) {
        mset(dp, 0);
        for (int i = 1; i <= m; i++) {
            sf2d(good[i].v, good[i].w);
            good[i].w = good[i].v * good[i].w;
        }


        for (int i = 1; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j - good[i].v >= 0) {
                    dp[i][j] = max(dp[i][j], dp[i-1][j - good[i].v] + good[i].w);//若没每件商品可以买多次,则为dp[i][j] = max(dp[i][j], dp[i][j - good[i].v] + good[i].w);
                }

            }
        }


        pfd(dp[m][n]);

    }


    return 0;
}

下面来优化一下:

#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include 
//#include
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#define INF 0x7fffffff
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define sf2d(x,y) scanf("%d %d",&(x),&(y))
#define sfd(x) scanf("%d",&x)
#define sff(p) scanf("%lf",&p)
#define pfd(x) printf("%d\n",x)
#define mset(x,b) memset((x),b,sizeof(x))
const double pi = acos(-1.0);
const long long mod = 1000000007LL;



//优化到一维数组
//dp[j]代表你手头有j元时最大的收益
int dp[30002];
int main(void) {
    int w[26], v[26];
    int m, n;
    while (sf2d(m, n) != EOF) {
        mset(dp, 0);
        for (int i = 1; i <= n; i++)
            sf2d(v[i], w[i]), w[i] *= v[i];

        for (int i = 1; i <= n; i++) {
            for (int j = m; j >= v[i]; j--) {
                dp[j] = max(dp[j], dp[j - v[i]] + w[i]);//把一维数组当作存在两个状态,
            }                                           //即一部分状态保存的是取到第i件物品时的状态,
        }                                               //一部分保存的是取到第i-1件物品时的状态,数组前段为i-1件物品,后段为i件物品

        pfd(dp[m]);
    }

    return 0;
}


空间需求变小了。

你可能感兴趣的:(01背包,洛谷oj,动态规划)