【noi 2.6_8787】数的划分(DP){附【转】整数划分的解题方法}

题意:问把整数N分成K份的分法数。(与“放苹果”不同,在这题不可以有一份为空,但可以类比)
解法:f[i][j]表示把i分成j份的方案数。
f[i][j]=f[i-1][j-1](新开一份,放1)
而i≥j时,f[i][j]=f[i-1][j-1]  +f[i-j][j](不新开一份时的方案数与每份中都少放1的方案数相同)

一种更好的解释——方法可以分为两类:
 1. n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 k 个 1 分。到每一份,然后再把剩下的 n- k 分成 k 份即可,分法有: dp[n-k][k]
  2. n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩下的 n- 1 再分成 k- 1 份即可,分法有:dp[n-1][k-1]

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

int f[210][10];
int main()
{
    int n,k;    
    scanf("%d%d",&n,&k);
    memset(f,0,sizeof(f));
    for (int i=1;i<=n;i++) f[i][1]=1;
    for (int i=1;i<=n;i++)
      for (int j=2;j<=k;j++)
        if (j<=i)
          f[i][j]=f[i-1][j-1]+f[i-j][j];
    printf("%d",f[n][k]);
    return 0;
}
View Code

 

附——整数划分的解题方法

{Article 1 转自——http://blog.csdn.net/luke2834/article/details/49429353 ;                                                   Article 2 转自——http://blog.csdn.net/athenaer/article/details/8265234}

其中一些问题具体实现见我另外几篇博文——http://www.cnblogs.com/konjak/p/6004018.html & http://www.cnblogs.com/konjak/p/6004636.html

我略修改了一下原博文,大家可比较一下。Article 1的方法很特别,Article 2的思路常规但也挺重要的。

 

问题描述给定一个正整数N和K
1. 将n划分成若干正整数之和的划分数。
2.将n划分成k个正整数之和的划分数。
3.将n划分成最大数不超过k的划分数。
4.将n划分成若干奇正整数之和的划分数。
5.将n划分成若干不同整数之和的划分数。
Article 1:总的来说这些都是背包问题;
第一个问,就是一个完全背包,背包有 1 --- N 种,第 i 种背包的重量为 i ,价值为 i ;这很好解决:

1 dp[0] = 1;
2 for (i = 1;i <= N;i++)
3   for (j = i;j <= N;j++)
4     dp[j] += dp[j-i];

其中 dp[j] 是用前 i 个数能构成 j 的种类数,则结果就为 dp[N]
看完这个问题了,那么 第3个问就知道了 , 即用前 K 种背包来装 所得结果,只需把第一层循环的 i <= N 改为 i <= K 即可;

1 dp[0] = 1;
2 for (i = 1;i <= K;i++)
3   for (j = i;j <= N;j++)
4     p[j] += dp[j-i];

那么第四个问呢,想想是奇数,那么 i = 2,4,6,…… 等等值就不能取了,因为这些背包种类不合要求,这很简单啊  i++ 改为 i += 2 不就行了;

1 dp[0] = 1;
2 for (i = 1;i <= N;i+=2)
3   for (j = i;j <= N;j++)
4     dp[j] += dp[j-i];

再看看第五个问,若干个不同的???想到了什么,就是一种背包最多只能用一次???这是什么,经典的 01背包 啊,与第一个问的不同就是第二层循环的顺序而已;

1 dp[0] = 1;
2 for (i = 1;i <= N;i++)
3   for (j = n;j >= i ;j--)
4     dp[j] += dp[j-i];

最后我们来思考第二个问:要求只要 K 个,这怎么办呢???想想特殊情况…… 如果 K = 1 呢,只能是 N 咯,若果 N = 0 呢, 结果只能是 0 种可能啊,那同样N < K 的话,不可能分啊。结果为 0 ,好,特殊的考虑完了,那么我们再考虑,分的结果中有没有 1 。 如果有1,那么就把剩下的 N - 1 分成 K - 1 份 ; 如果没有呢,那么我们先拿出 K 份 给每一堆 一个1, 再把剩下的 N - K 分成 K 份就行了。

Article 2:整数划分 --- 一个老生长谈的问题:     

1.将n划分成不大于m的划分法: P.S.依问题定义——不定义划分成多少份。

   1).若是划分多个整数可以存在相同的:

    dp[n][m]= dp[n][m-1]+ dp[n-m][m]  dp[n][m]表示整数 n 的划分中,每个数不大于 m 的划分数。
      分两种情况:
       a.划分中每个数都小于 m,相当于每个数不大于 m- 1, 故划分数为 dp[n][m-1].
       b.划分中有一个数为 m. 那就在 n中减去 m ,剩下的就相当于把 n-m 进行划分, 故划分数为 dp[n-m][m];

  2).若是划分多个不同的整数:

  dp[n][m]= dp[n][m-1]+ dp[n-m][m-1]   dp[n][m]表示整数 n 的划分中,每个数不大于 m 的划分数。
    分两种情况:
      a.划分中每个数都小于m,相当于每个数不大于 m-1,划分数为 dp[n][m-1].
      b.划分中有一个数为 m.在n中减去m,剩下相当对n-m进行划分,

   并且每一个数不大于m-1,故划分数为 dp[n-m][m-1]

2.将n划分成k个数的划分法:

        dp[n][k]= dp[n-k][k]+ dp[n-1][k-1];

     方法可以分为两类:
     第一类: n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 k 个 1 分。到每一份,然后再把剩下的 n- k 分成 k 份即可,分法有: dp[n-k][k]
     第二类: n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩下的 n- 1 再分成 k- 1 份即可,分法有:dp[n-1][k-1]

3.将n划分成若干奇数的划分法:

          g[i][j]:将i划分为j个偶数;f[i][j]:将i划分为j个奇数
     g[i][j] = f[i - j][j];f[i][j] = f[i - 1][j - 1] + g[i - j][j];
         方法可以分为两类:
       第一类:i中拿出j个1分到每一份中,将剩余的i-j分成j个奇数
       第二类:一份包含奇数1,剩余的i-1分成j-1个奇数;另一种,每份至少大于1,将j个1拿出来分到每一份中,其余i-j分成j份

posted @ 2016-10-11 21:49  konjac蒟蒻  阅读(2041)  评论(0编辑  收藏  举报