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

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/90679428

题目:POJ1742.
题目大意:给定 n n 种物品,物品 i i 价值为 A i A_i ,数量为 C i C_i ,问 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 .

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

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

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

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

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

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
  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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/90679428