sumsets(基础动态规划)

Farmer John commanded his cows to search for different sets of numbers that sum to a given number. The cows use only numbers that are an integer power of 2. Here are the possible sets of numbers that sum to 7:

1) 1+1+1+1+1+1+1
2) 1+1+1+1+1+2
3) 1+1+1+2+2
4) 1+1+1+4
5) 1+2+2+2
6) 1+2+4

Help FJ count all possible representations for a given integer N (1 <= N <= 1,000,000).
Input
A single line with a single integer, N.
Output
The number of ways to represent N as the indicated sum. Due to the potential huge size of this number, print only last 9 digits (in base 10 representation).
Sample Input
7
Sample Output
6
dp[i]:i的划分数。
dp[i]=dp[i-1]                //i为奇数
dp[i]=dp[i-1]+dp[i/2]    //i为偶数
边界控制:dp[1]=1


当i为奇数
很好理解,奇数是1产生的吧,每个划分里面必须都要有1,把这个1减去,就是偶数i-1的划分。把7的每个划分都减去1不就是6的吗?
i=7
1) 1+1+1+1+1+1+1
2) 1+1+1+1+1+2
3) 1+1+1+2+2
4) 1+1+1+4
5) 1+2+2+2
6) 1+2+4


当i为偶数,把所有的划分 分成两组,带1的和不带1的。带1的不又是比原偶数(6)小1的奇数(5)的划分了吗?不带1的划分里面肯定含有2吧?同时除2,不又产生1了吗(也就是说,又是一个完整的划分了)?
i=6
1) 1+1+1+1+1+1
2) 1+1+1+1+2
3) 1+1+2+2
4) 1+1+4
5) 2+2+2    (1+1+1)
6) 2+4        (1+2)不就是3的所有(完整性)划分了吗?

感觉这个规律总结的十分的巧妙了。如果没有总结出这个规律的也还有完全背包的做法


dp[i][j]:只用前i个数,j的组合数

dp[i][j]=∑dp[i-1][j-k*w[i]]   k>=0;
含义:先从前i-1个数里面组合成j-k*w[i].再从第i个里面拿出来k个。有点完全背包的意思。
化简:
原式=∑dp[i-1][j-k*w[i]]                      k>=0;
    =dp[i-1][j]+∑dp[i-1][j-k*w[i]]           k>=1;
    =dp[i-1][j]+∑dp[i-1][j-w[i]-(k-1)*w[i]]  k-1>=0;
    =dp[i-1][j]+dp[i][j-w[i]]

dp[i][j]=dp[i-1][j]+dp[i][j-w[i]]

边界控制dp[1][j]=1;

方法1的代码

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define mod 1000000000
using namespace std;
long long dp[1000010];
int main()
{
    int n;
    ios::sync_with_stdio(false);
    cin>>n;
    dp[1]=1;
    if(n>1){
    for(int i=2;i<=n;i++){
        if(i&1){
            dp[i]=dp[i-1];
        }
        else {
            dp[i]=(dp[i-1]+dp[i/2])%mod;
        }
    }
    }
    cout<<dp[n]<<endl;
    return 0;
}

方法2的代码

  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<algorithm>  
  4.   
  5. using namespace std;  
  6. long w[21];//2的20次方刚好比最大数据多一点  
  7. int dp[1000005];  
  8. const long long MOD = (int)1e9;  
  9. int main()  
  10. {  
  11.     int N;    
  12.     w[1] = 1;  
  13.     for (int i = 2; i <= 20; i++)  //20个物品,最后一个为2^19;  
  14.         w[i] = w[i - 1] * 2;  
  15.     while( cin >> N)  
  16.     {  
  17.     fill(dp, dp + N + 1, 1);  
  18.     for (int i = 2; i <= 20; i++)  
  19.         for (int j = w[i]; j<=N; j++)  
  20.         {  
  21.             dp[j] += dp[j - w[i]];  
  22.             dp[j] %= MOD;  
  23.         }  
  24.     cout << dp[N] << endl;  
  25.     }  
  26.     system("pause");  


猜你喜欢

转载自blog.csdn.net/sinat_40948489/article/details/80526709