P3092 [USACO13NOV]没有找零No Change

${\color{Cyan}{>>Question}}$

一道状压好题(少有的没看题解一遍$AC$的不是水题(自认为)的题(看来是我太菜了))

首先看到$k\leqslant 16$,$emm$,要么暴搜,要么状压

再看$n\leqslant 10^5$,$emm$,状压实锤了,但这$n$太大,显然其中状压$dp$的状态和转移中都不能有$n$(一有就炸了)

看范围先想一维,令$f[i]$表示状态$i$下的什么

P4329 [COCI2006-2007#1] Bond的经验,我们知道状态并不只表示了其中的选或不选,还可以表示选了多少个和选了哪些

在这道题下,选了哪些就尤为重要了(因为就可以统计答案了),那差的就是能不能买到$n$个或者说能买多少个

试着令$f[i]$表示状态$i$下最多能买多少个,枚举当前用哪个硬币(假设是第$j$号硬币)

那之前的状态为$f[i-\left \{ j \right \}]$(也就是说$j$号硬币要从$f[i-\left \{ j \right \}]+1$号物品开始买),

那现在的问题就只剩下$j$号硬币从某处开始最多能买多少个物品,这可以用前缀和$+$二分提前预处理出来

令$cnt[i][j]$表示$i$号硬币从$j$处开始最多买多少个,$r$表示最远的那个编号

显然有$$sum[r]-sum[j-1]\leqslant val[i]$$

即$$r=upperbound(val[i]+sum[j-1])-1$$

如此,转移就出来了$$f[i] = max\left \{ f[i-\left \{ j \right \}]+cnt[j][f[i-\left \{ j \right \}]+1] \right \}$$

上代码(写得好累啊$QwQ$)

 

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #define ll long long
 6 using namespace std; 
 7 
 8 template <typename T> void in(T &x) {
 9     x = 0; T f = 1; char ch = getchar();
10     while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
11     while( isdigit(ch)) {x = 10 * x + ch - 48; ch = getchar();}
12     x *= f;
13 }
14 
15 template <typename T> void out(T x) {
16     if(x < 0) x = -x , putchar('-');
17     if(x > 9) out(x/10);
18     putchar(x%10 + 48);
19 }
20 //-------------------------------------------------------
21 
22 const int N = 1e6+5,M = 17;
23 
24 int k,n;
25 int val[M],cst[M],cnt[M][N];
26 ll sum[N];
27 int f[1<<M];
28 ll ans = -1;
29 
30 int main() {
31     int i,j;
32     in(k); in(n);
33     for(i = 1;i <= k; ++i) in(val[i]);
34     for(i = 1;i <= n; ++i) in(cst[i]),sum[i] = sum[i-1]+cst[i];
35     for(i = 1;i <= k; ++i) {
36         for(j = 1;j <= n; ++j) {
37             cnt[i][j] = upper_bound(sum+1,sum+n+1,val[i]+sum[j-1])-sum-1-j+1;//r-j+1//debug val -> cst
38         }
39     }//预处理出i硬币在位置j向后延伸的最长长度
40     memset(f,~0x3f,sizeof(f));
41     f[0] = 0;
42     for(i = 1;i < (1<<k); ++i) {
43         for(j = 1;j <= k; ++j) {
44             if(i&(1<<(j-1))) f[i] = max(f[i],f[i^(1<<(j-1))]+cnt[j][f[i^(1<<(j-1))]+1]);
45         }
46         if(f[i] >= n) {
47             ll res = 0;
48             for(j = 1;j <= k; ++j) if(!(i&(1<<(j-1)))) res += val[j];
49             ans = max(res,ans);
50         }
51     }
52     out(ans);
53     return 0;
54 }

 

posted @ 2019-08-15 20:16  陈星卿  阅读(180)  评论(0编辑  收藏  举报