交大OJ 1069 二哥的硬币(多重背包/单调队列)

  1. 二哥的硬币
    Description
    快放假了,二哥想给女朋友买一个礼物。
    走到商店前,发现钱包里只有硬币了。二哥数了一下,一共有n种硬币,面值分别为A1, A2, …, An,每种硬币的个数分别为C1, C2, …, Cn。
    二哥心里没有底,他估计要买的礼物价格不会超过m,但不知道到底要买多少钱的礼物。
    二哥的硬币已经很多了,他不想再要更多的硬币了,所以他想知道,用手头这些硬币,可以正好凑出1到m中多少种金额(正好相等,包括1和m)。
    Input Format
    输入包含多组测试数据。
    每组测试数据的第一行是空格分隔的两个整数n和m,
    1≤n≤100,1≤m≤100000
    1≤n≤100,1≤m≤100000

    接下来有2n个整数,分别是面值A1, A2, …, An,以及硬币个数C1, C2, …, Cn,用空白分隔,
    1≤Ai≤100000,1≤Ci≤1000
    1≤Ai≤100000,1≤Ci≤1000

    当读入的n=0且m=0时,表示输入结束;这组数据不需要处理。
    Output Format
    对于每组测试数据,输出用这些硬币可以正好凑出1到m范围内的多少种金额。
    说明
    http://poj.org/problem?id=1742
    Sample Input
    2 5
    1 4 2 1
    3 10
    1 2 4 2 1 1
    0 0
    Sample Output
    4
    8

代码如下:

#include 
#include 
using namespace std;
const long long M = 100001;//m<=100000
bool dpall[M];//A[i]:the combination of coins valuing i exists.
long long A[101];
long long C[101];
long long alldata[M];
int coins = 0;
long long sets = 0;
long long m;
void setdpallahead(){
  for (int i = 0; i < M; i++) {
    dpall[i] = false;
    alldata[i] = 0;
  }
}

void setdp(){
  sets = 0;
  setdpallahead();
  for (int i = 1; i <= coins; i++) {//start from 1.
    if (i==1) {
      for(int j=1; j<=C[1]&&j*A[1]<=m; j++){
        dpall[j*A[1]] = true;
        alldata[++sets] = j*A[i];
      }
    }//deal with the condition that j==1.
    else{//deal with the condition that j>1.
      long long prevsets = sets;//present scale
      for (int j = 0; j <= prevsets ; j++) {
        for (int k = 1; k<=C[i]; k++) {
          if (alldata[j]+k*A[i]>m) {//alldata[0]==0,so you can take 0+k*A[i] into thought
            break;//finish the preceding "for" recycle.
          }
          if (!dpall[alldata[j]+k*A[i]]) {//this step records that the value alldata[j]+k*A[i]] exists.
            dpall[alldata[j]+k*A[i]] = true;
            alldata[++sets] = alldata[j]+k*A[i];
          }
        }
      }
    }
  }
}

int main(){
  scanf("%d%lld", &coins, &m);
  while (coins!=0||m!=0) {
    for (int i = 1; i <= coins; i++) {
      scanf("%lld", &A[i]);
    }
    for (int i = 1; i <= coins; i++) {
      scanf("%lld", &C[i]);
    }
    setdp();
    printf("%lld\n", sets);
    scanf("%d%lld", &coins, &m);
  }
  return 0;
}

多重背包+单调序列:
dpall[i]:记录面值为i的硬币组合是否存在,相当于查找功能,可减少查找算法的时间
alldatas[]:范围用sets存储,alldata[0]=0,面值组合从alldata[1]开始存储,只记录
<=m的面值数额;
sets:存储<=m的面值数值,sets也是alldata中记录的有效长度。

你可能感兴趣的:(OJ)