zoukankan      html  css  js  c++  java
  • LOJ6039. 「雅礼集训 2017 Day5」珠宝【决策单调性优化DP】【分治】【思维好题】

    LINK


    懒得搬题面
    简要题意:n个物品,每个物品有一个价格和一个吸引力,问你对于(i in [1,k]),花费i的价格能得到的最大吸引力

    其中价格的范围很小,在([1,300])范围内

    思路

    首先想到一个dp
    (dp_{i,j})表示用对于价格小于等于i的物品花费j的价格能得到的最大吸引力
    然后对于相等的价格的物品,有一个贪心的的思想就是个数确定的时候选取最大吸引力的几个
    这个可以前缀和预处理出来
    然后考虑对于价格相等的时候
    如果对于(dp_{w,p}的决策点i < j),存在(dp_{w-1,i} + sum[i->p] < dp_{w-1,j} + sum[j->p])
    那么因为很显然sum这个函数是斜率单调递减的所以可以得到对于任何(dp_{w,-}),i一定不会比j更优
    所以这样对于w相同的情况就可以处理了
    然是题目中的w不完全相同怎么办?
    那就强行把w变成相同的就可以了对于(dp_{w,-})我们只考虑不够w的补上多余的价格变成w就可以了
    然是这样有可能算不完全,所以对于每一个小于c的数都需要当成决策点来计算一遍
    然后一个决策点只会从和它对于c同余相等的决策点转移
    这样一定是最优的
    然后直接for计算所有可能的决策点
    但是为什么需要分治算法呢?
    因为虽然有决策点单调的性质,但是我们并不能快速计算出决策点
    所以可以考虑分治,枚举中间的决策点然后递归成两个区间分别求解,这样的复杂度是有保障的


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    //typename
    typedef long long ll;
    //convenient for
    #define for_up(a, b, c) for (int a = b; a <= c; ++a)
    #define for_down(a, b, c) for (int a = b; a >= c; --a)
    #define for_vector(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    //inf of different typename
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    //fast read and write
    template <typename T>
    void Read(T &x){
      bool w = 1;x = 0;
      char c = getchar();
      while(!isdigit(c) && c != '-')c = getchar();
      if(c == '-')w = 0, c = getchar();
      while(isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if(!w)x=-x;
    }
    template <typename T>
    void Write(T x){
      if(x < 0) {
        putchar('-');
        x=-x; 
      }
      if(x > 9)Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    const int N = 1e6 + 10;
    const int M = 5e4 + 10;
    const int C = 3e2 + 10; 
    int n, k, maxc = 0;
    int c[N], v[N];
    int ind, table[N];
    ll dp[C][M];
    vector<ll> bag[C], buc[C];
    bool cmp(int a, int b) { return a > b;}
    ll calc(int id, int lastpos, int now) { // 计算一个决策点的贡献
      int num = (now - lastpos) / id;
      if (num > (signed)bag[id].size() - 1) return -1;
      return dp[id - 1][lastpos] + bag[id][num];
    }
    void solve(int id, int l, int r, int pl, int pr) {
      int mid = (l + r) >> 1;
      int pos = 0; ll maxv = 0;
      for_up(i, pl , min(mid, pr)) { // 当前决策点可能的区间
        ll val = calc(id, table[i], table[mid]); 
        if (val > maxv) maxv = val, pos = i; // 记录最大权值和决策点
      }
      if(l == r) { // 如果已经到达一个节点就更新答案
        dp[id][table[l]] = maxv;
        return;
      }
      // 因为决策单调 所以决策点可以进行划分求解
      // 总复杂度是有保证的
      solve(id, l, mid, pl, pos);
      solve(id, mid + 1, r, pos, pr);
    }
    int main() {
      memset(dp, 0, sizeof(dp));
      Read(n), Read(k);
      int maxc = 0;
      for_up(i, 1, n) {
        Read(c[i]), Read(v[i]);
        buc[c[i]].push_back(v[i]);
        maxc = max(maxc, c[i]); 
      }
      for_up(i, 1, maxc) {
        sort(buc[i].begin(), buc[i].end(), cmp);
        bag[i].push_back(0);
        ll now = 0;
        for_vector(j, buc[i]) {
          now += buc[i][j];
          bag[i].push_back(now);
        }
      }
      for_up(i, 1, maxc) {// 考虑单个售价是小于等于i的物品
        for_up(j, 0, i - 1) {// 考虑对mod i余j的所有转移
          table[ind = 1] = j;
          while (table[ind] + i <= k) {
            table[ind + 1] = table[ind] + i;
            ++ind;
          }
          solve(i, 1, ind, 1, ind);// 分治求解
        }
      }
      for_up(i, 1, k) {
        Write(dp[maxc][i]);
        putchar(' ');
      }
      return 0;
    }
    
  • 相关阅读:
    Scala课程01
    深入分析面向对象中的对象概念(转)
    代码审查时,发现功能实现的原因,而不仅仅是挑毛病(转)
    独立开发者复盘:手游研发犯过的8个错误(转)
    HTTPS背后的加密算法(转)
    How to recover from 'programmers burnout(转)
    数据流图的画法
    Filter及FilterChain的使用具体解释
    SimpleDateFormat使用具体解释
    TCP/IP协议,HTTP协议
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9733229.html
Copyright © 2011-2022 走看看