1. 程式人生 > >POJ 3181 組合數+大數的處理

POJ 3181 組合數+大數的處理

Dollar Dayz

Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. During his first visit, the tools are selling variously for $1, $2, and $3. Farmer John has exactly $5 to spend. He can buy 5 tools at $1 each or 1 tool at $3 and an additional 1 tool at $2. Of course, there are other combinations for a total of 5 different ways FJ can spend all his money on tools. Here they are: 

        1 @ US$3 + 1 @ US$2

        1 @ US$3 + 2 @ US$1

        1 @ US$2 + 3 @ US$1

        2 @ US$2 + 1 @ US$1

        5 @ US$1

Write a program than will compute the number of ways FJ can spend N dollars (1 <= N <= 1000) at The Cow Store for tools on sale with a cost of $1..$K (1 <= K <= 100).

Input

A single line with two space-separated integers: N and K.

Output

A single line with a single integer that is the number of unique ways FJ can spend his money.

Sample Input

5 3

Sample Output

5

求1到k中能夠組合為n的最組合數

由於k的個數是無限制的,這和完全揹包比較相似,因此就需要推一下dp的狀態轉移方程了,在這裡定義一下dp[i][j]表示當前值為i的時候用1到j的組合數的最大值,我們很容易想到

1.dp[i][1]=1,還有就是當i<j的時候 dp[i][j]=dp[i][j-1];

2.當i>j的時候就需要思考一下,可以手動模擬前6個,就可以看出當推dp[i][j]的時候,dp[i][j]是由前j個數推出的,即dp[i][j-1],還有一部分,那就是dp[i-j][j],為什麼?因為dp[i][j]也是在dp[i-j][j]的基礎上加上jz所得出的,因此dp[i][j]=dp[i][j-1]+dp[i-j][j];

但是這樣還是不能ac,因為這樣di遞推的話連long long都會越界,因此只能夠用兩個long long 型別的陣列連線一下,這樣就構成前19位,後19位了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll pre[1010][105];
ll back[1010][105];
const int INF=0x3f3f3f3f;
#define rep(i,a,b) for(int i=a;i<=b;i++)
int main(){
    int n,k;
    ll M=1;
    rep(i,1,18) M*=10;
    scanf("%d%d",&n,&k);
    rep(i,0,k)
    back[0][i]=1;
    rep(j,1,k)
    {
        rep(i,1,n)
        {
            if(j>i)
            {
                pre[i][j]=pre[i][j-1];back[i][j]=back[i][j-1];
            }
            else{
                pre[i][j]=pre[i-j][j]+pre[i][j-1]+(back[i-j][j]+back[i][j-1])/M;
                back[i][j]=(back[i-j][j]+back[i][j-1])%M;
            }
        }
    }
    if(pre[n][k]) printf("%I64d",pre[n][k]);
    printf("%I64d\n",back[n][k]);
}

還可以優化

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll pre[1050];
ll back[1050];
const int INF=0x3f3f3f3f;
#define rep(i,a,b) for(int i=a;i<=b;i++)
int main(){
    int n,k;
    ll M=1;
    rep(i,1,18) M*=10;
    scanf("%d%d",&n,&k);
    back[0]=1;
    rep(j,1,k )
    {
           rep(i,1,n)
        {
            if(j>i) continue;
            else{
                pre[i]=pre[i-j]+pre[i]+(back[i-j]+back[i])/M;
                back[i]=(back[i-j]+back[i])%M;
            }
        }
    }
    if(pre[k]) printf("%I64d",pre[n]);
    printf("%I64d\n",back[n]);
}