链接: https://vjudge.net/contest/176436#problem/B
题目大意:
有各种不同面值的货币,每种面值的货币有不同的数量,请找出利用这些货币可以凑成的最接近且小于等于给定的数字cash的金额。
用v*∑num【i】的算法肯定超时,因为这里v太大,num【i】也不小
超时代码:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main()
{
    int m,n;
    int v[1999],num[1999],w[1999];
    int dp[200000];
    while(cin>>m>>n)
    {

        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&num[i],&w[i]);

        }
        //memset(dp,0,sizeof(dp));
        int mmax = 0;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<num[i];j++)
            {
                for(int k=m;k>=w[i];k--)
                {
                    dp[k] = max(dp[k],dp[k-w[i]]+w[i]);
                    mmax = max(mmax,dp[k]);
                }

            }
        }
        cout<<mmax<<endl;
    }
    return 0;
}

在完全背包的基础上更改一下就得到了正确代码

//多重背包
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<iostream>
#include<set>
#include<iomanip>
using namespace std;
int n,v;
int mmax;
int a[111111];
int dp[600100];
int c[600100];
const int inf = 0x3f3f3f3f;
int num[6010],w[6100];
int main()
{

     while(cin>>v>>n)
     {
         for(int i =0;i<n;i++)
         {
             scanf("%d%d",&num[i],&w[i]);
         }

         memset(dp,0,sizeof(dp));
         for(int i=0;i<n;i++)
         {
             memset(c,0,sizeof(c));
             for(int j=w[i];j<=v;j++)
             {
                 if(dp[j]<dp[j-w[i]]+w[i]&&c[j-w[i]]<num[i])
                 {
                     dp[j] = dp[j-w[i]]+w[i];
                     c[j] = c[j-w[i]]+1;
                 }
             }
         }
         printf("%d\n",dp[v]);
     }
     return 0;
}

Xuhuang在挑战程序设计竞赛上改编了一道类似的题,于是得到了第三种算法:
改编自是否存在正好等于v的一道背包题

//多重背包
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<iostream>
#include<set>
#include<iomanip>
using namespace std;
int n,v;
int mmax;
int a[111111];
int dp[600100];
int c[600100];
const int inf = 0x3f3f3f3f;
int num[6010],w[6100];
int main()
{

     while(cin>>v>>n)
     {
         for(int i =0;i<n;i++)
         {
             scanf("%d%d",&num[i],&w[i]);
         }

        memset(dp,-1,sizeof(dp));
        dp[0] = 0;
        for(int i=0;i<n;i++)
        {

            for(int j=0;j<=v;j++)
            {
               if(dp[j]>=0)dp[j] = num[i];
               else if(j<w[i]||dp[j-w[i]]<=0)
               {
                   dp[j] = -1;
               }
               else
                dp[j] = dp[j-w[i]]-1;
            }
        }
        int i;
        for( i=v;i>=0;i--)
        {
            if(dp[i]>=0)break;
        }
        printf("%d\n",i);
     }
     return 0;
}

此外还有一种二进制算法,贴在这里供大家参考

http://blog.csdn.net/weinierzui/article/details/23671419