【大疆2019秋招在线笔试】算法第一题参考思路

题目

给定暑假时间X天(<=1000),游戏数量N个(<=11),接下来N行给定每种游戏需要花费的天数(Ai),以及通关该游戏带来的成就点数(Bi),求:在暑假X天里能够达成的最高成就点数。

总体思路

首先对游戏进行排序(时间从短到长,价值从低到高),然后就是标准的01背包问题了。

背包问题的解决过程

在解决问题之前,为描述方便,首先定义一些变量:Vi表示第 i 个物品的价值,Wi表示第 i 个物品的体积,定义V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的价值,同时背包问题抽象化(X1,X2,…,Xn,其中 Xi 取0或1,表示第 i 个物品选或不选)。

1、建立模型,即求max(V1X1+V2X2+…+VnXn);

2、寻找约束条件,W1X1+W2X2+…+WnXn<capacity;

3、寻找递推关系式,面对当前商品有两种可能性:

包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即V(i,j)=V(i-1,j);
还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}。
其中V(i-1,j)表示不装,V(i-1,j-w(i))+v(i) 表示装了第i个商品,背包容量减少w(i),但价值增加了v(i);

由此可以得出递推关系式:

j<w(i) V(i,j)=V(i-1,j)
j>=w(i) V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}
这里需要解释一下,为什么能装的情况下,需要这样求解(这才是本问题的关键所在!):

可以这么理解,如果要到达V(i,j)这一个状态有几种方式?

肯定是两种,第一种是第i件商品没有装进去,第二种是第i件商品装进去了。没有装进去很好理解,就是V(i-1,j);装进去了怎么理解呢?如果装进去第i件商品,那么装入之前是什么状态,肯定是V(i-1,j-w(i))。由于最优性原理(上文讲到),V(i-1,j-w(i))就是前面决策造成的一种状态,后面的决策就要构成最优策略。两种情况进行比较,得出最优。

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <map>
using namespace std;

#define MAX_N 10000

typedef struct Node
{
    int t;
    int val;
    friend bool operator < (Node a, Node b)
    {
        if (a.t == b.t)
        {
            return a.val < b.val;
        }
        return a.t < b.t;
    }
}node;
int main() 
{
    int T, N, X;
    node game[MAX_N];
    cin >> T;
    for (int ca = 0; ca < T; ca++)
    {
        int dp[1001][12] = { {0} };
        //dp = (int(*)[MAX_N])malloc(N * MAX_N * sizeof(int));
        //memset(dp, 0, 1001 * 12 * sizeof(int));
        memset(game, 0, MAX_N * sizeof(node));
        cin >> N >> X;
        for (int i = 0; i < N; i++)
        {
            cin >> game[i].val >> game[i].t;
        }
        sort(game, game + N);
        for (int i = game[0].t; i <= X; i++)
        {
            for (int j = 1; j <= N; j++)
            {
                if (game[j].t <= i)
                {
                    dp[i][j] = max(dp[i - game[j].t][j] + game[j].val, dp[i][j - 1]);
                }
            }
        }
        cout << dp[X][N] << endl;
    }
    return 0;
}
全部评论
根本就不需要排序。直接就是0-1背包问题啊。而且我一直不明白为什么你们总是一开始就开辟那么大的数组空间?为什么都不用vector? #include <iostream> #include <vector> #include <cassert> #include <algorithm> using namespace std; // 需要填充一个容量为X的背包,使得成就点数最大 class Knapsack01 { private: vector<vector<int>> memo; // 用 [0...index]的物品,填充容积为c的背包的最大价值 int bestValue(const vector<int> &w, const vector<int> &v, int index, int c) { if (c <= 0 || index < 0) return 0; if (memo[index][c] != -1) return memo[index][c]; int res = bestValue(w, v, index - 1, c); if (c >= w[index]) res = max(res, v[index] + bestValue(w, v, index - 1, c - w[index])); memo[index][c] = res; return res; } public: int knapsack01(const vector<int> &w, const vector<int> &v, int C) { assert(w.size() == v.size() && C >= 0); int n = w.size(); if (n == 0 || C == 0) return 0; memo.clear(); for (int i = 0; i < n; i++) memo.push_back(vector<int>(C + 1, -1)); return bestValue(w, v, n - 1, C); } }; int main() { // X为暑假天数,N为游戏数量 int X, N; cin >> X >> N; int w, v; // vs存的是价值(成就点数) // ws存的是每一件物品的重量(天数) vector<int> vs, ws; for (int i = 0; i < N; i++) { cin >> w >> v; vs.push_back(v); ws.push_back(w); } cout << Knapsack01().knapsack01(ws, vs, X) << endl; return 0; }
1 回复
分享
发布于 2019-08-12 15:20

相关推荐

点赞 1 评论
分享
牛客网
牛客企业服务