Coins(揹包DP)

Description
People in Silverland use coins.They have coins of value A1,A2,A3…An Silverland dollar.One day Tony opened his money-box and found there were some coins.He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn’t know the exact price of the watch.
You are to write a program which reads n,m,A1,A2,A3…An and C1,C2,C3…Cn corresponding to the number of Tony’s coins of value A1,A2,A3…An then calculate how many prices(form 1 to m) Tony can pay use these coins.
Input
The input contains several test cases. The first line of each test case contains two integers n(1<=n<=100),m(m<=100000).The second line contains 2n integers, denoting A1,A2,A3…An,C1,C2,C3…Cn (1<=Ai<=100000,1<=Ci<=1000). The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
Sample Output
8
4

n種硬幣,每種硬幣ai價值,ci個,問能用其表示的數有多少個。

直接多重揹包二進制優化,然後ttttt成一個傻逼,最後能手寫的都手寫,全部掛上手動擋終於卡了過去- -。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<utility>
#include<sstream>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
int f[100005],a[200],c[200];
int ans;
void Zeroonepack(int c,int v)
{
    for(int i = v;i >= c;i--)
    {
        if(f[i])continue;
        if(f[i - c])
        {
            f[i] = 1;ans++;
        }
    }
}
void Completepack(int c,int v)
{
    for(int i = c;i <= v;i++)
    {
        if(f[i])continue;
        if(f[i - c])
        {
            f[i] = 1;ans++;
        }
    }
}
void Multiplepack(int c,int num,int v)
{
    if(c*num >= v)
        Completepack(c,v);
    else
    {
        int k = 1;
        while(k < num)
        {
            Zeroonepack(k*c,v);
            num -= k;
            k <<= 1;
        }
        Zeroonepack(num*c,v);
    }
}
int main()
{
    #ifdef LOCAL
    freopen("C:\\Users\\ΡΡ\\Desktop\\in.txt","r",stdin);
    //freopen("C:\\Users\\ΡΡ\\Desktop\\out.txt","w",stdout);
    #endif // LOCAL
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        if(!n&&!m)break;
        for(int i = 1;i <= n;i++)
            scanf("%d",&a[i]);
        for(int i = 1;i <= n;i++)
            scanf("%d",&c[i]);
        for(int i = 1;i <= m;i++)f[i] = 0;
        f[0] = 1;
        ans = 0;
        for(int i = 1;i <= n;i++)
            Multiplepack(a[i],c[i],m);
        printf("%d\n",ans);
    }
    return 0;
}

然而挑戰上還有一種解法,dp[i][j]表示前i種硬幣表示價值j之後剩下第i種硬幣的個數,這種題目一般都是yes,no的bool型,用這種思路得到的信息更多。
若dp[i - 1][j]不是-1,說明前i - 1種硬幣就可以表示j,那dp[i][j] = num[i],若dp[i][j - a[i]] <= 0或是j < a[i]那麼dp[i][j]爲-1。
否則dp[i][j] = dp[i][j - 1] - 1;
再觀察表達式,發現第一維是可以省略的。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<utility>
#include<sstream>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
struct Node
{
    int a,c;
}node[105];
int dp[100005];
int main()
{
    #ifdef LOCAL
    freopen("C:\\Users\\ΡΡ\\Desktop\\in.txt","r",stdin);
    //freopen("C:\\Users\\ΡΡ\\Desktop\\out.txt","w",stdout);
    #endif // LOCAL
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        if(!n&&!m)break;
        for(int i = 1;i <= n;i++)
            scanf("%d",&node[i].a);
        for(int i = 1;i <= n;i++)
            scanf("%d",&node[i].c);
        memset(dp,-1,sizeof(dp));
        dp[0] = 0;
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j <= m;j++)
            {
                if(dp[j] >= 0)
                    dp[j] = node[i].c;
                else if(j < node[i].a || dp[j - node[i].a] <= 0)
                    dp[j] = -1;
                else
                    dp[j] = dp[j - node[i].a] - 1;

            }
        }
        int ans = 0;
        for(int i = 1;i <= m;i++)
            if(dp[i] >= 0)ans++;
        printf("%d\n",ans);
    }
    return 0;
}

2000ms過得,還算可以- -

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章