蓝书(算法竞赛进阶指南)刷题记录——POJ1742 Coins(DP+贪心)

题目:POJ1742.
题目大意:给定 n n n种物品,物品 i i i价值为 A i A_i Ai,数量为 C i C_i Ci,问 1   m 1~m 1 m之间多少种面值能被凑出来.
1 ≤ n ≤ 100 , 1 ≤ m , A i ≤ 1 0 5 , 1 ≤ C i ≤ 1 0 3 1\leq n\leq 100,1\leq m,A_i\leq 10^5,1\leq C_i\leq 10^3 1n100,1m,Ai105,1Ci103.

一道多重背包的模板,当然可以用二进制拆分或者单调队列优化做,但是感觉这样做很麻烦.

考虑这个问题的特殊性,发现它只是要求可行性,我们是否可以从这里入手优化呢?

若当前正在枚举第 i i i种物品, f [ j ] f[j] f[j]表示价值 j j j是否能够被拼凑出来,容易发现前面 i − 1 i-1 i1种物品造成的影响已经没用了,我们只需要考虑当前第 i i i种物品造成的影响即可.

贪心的想,容易发现如果 j j j的价值可以只通过 k k k枚第 i i i种物品凑出来,我们肯定不考虑比 k k k枚更多的硬币,所以考虑记录一下使 f [ j ] = 1 f[j]=1 f[j]=1最少需要物品 i i i的数量 u s e [ j ] use[j] use[j].每次枚举到第 i i i种物品的时候先把 u s e use use数组初始化为 0 0 0,转移的时候若发现 f [ j ] = 1 f[j]=1 f[j]=1 f [ j − a [ i ] ] = 0 f[j-a[i]]=0 f[ja[i]]=0 u s e [ j − a [ i ] ] > = c [ i ] use[j-a[i]]>=c[i] use[ja[i]]>=c[i]时就不管了,否则就让 f [ j ] = 1 , u s e [ j ] = u s e [ j − a [ i ] ] + 1 f[j]=1,use[j]=use[j-a[i]]+1 f[j]=1,use[j]=use[ja[i]]+1.

时间复杂度 O ( n m ) O(nm) O(nm).

代码如下:

#include
#include
#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100,M=100000;

int n,m,a[N+9],c[N+9];
int dp[M+9],use[M+9],ans;

Abigail into(){
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
  for (int i=1;i<=n;++i)
    scanf("%d",&c[i]); 
}

Abigail work(){
  for (int i=1;i<=m;++i) dp[i]=0;
  dp[0]=1;
  for (int i=1;i<=n;++i){
    for (int j=0;j<=m;++j) use[j]=0;
    for (int j=a[i];j<=m;++j)
      if (!dp[j]&&dp[j-a[i]]&&use[j-a[i]]<c[i]) dp[j]=1,use[j]=use[j-a[i]]+1;
  }
  ans=0;
  for (int i=1;i<=m;++i) ans+=dp[i];
}

Abigail outo(){
  printf("%d\n",ans);
}

int main(){
  while (~scanf("%d%d",&n,&m)&&n+m){
    into();
    work();
    outo();
  }
  return 0;
}

你可能感兴趣的:(蓝书(算法竞赛进阶指南)刷题记录——POJ1742 Coins(DP+贪心))