整数划分问题模型

整数划分问题模型

问题一: 求把n划分成k个正整数的方案数?

暴力dp:(暴力dp要做到不重不漏)
设dp[i][j][sum]表示前i个正整数,选了j个数,和为sum的方案数,答案是:dp[n][k][n]
本质:完全背包
复杂度:O(n*k*n)
正解:
直接设dp[i][j]表示把i划分成j个数的方案数
则dp[i][j]=dp[i-j][j]+dp[i-1][j-1](可从数形结合或者抽屉原理的方面来考虑)

问题二:求把n划分成互不相同k个正整数的方案数?

暴力dp:
设dp[i][j][sum]表示前i个正整数,选了j个数,和为sum的方案数,答案是:dp[n][k][n]
本质:0/1背包
复杂度:O(n*k*n)
正解:
直接设dp[i][j]表示把i划分成j个数的方案数
则dp[i][j]=dp[i-j][j]+dp[i-j][j-1](可从数形结合或者抽屉原理的方面来考虑)

问题三:求把n划分成k个不大于m的互不相同正整数的方案数?

直接上正解:
设dp[i][j]表示把i划分成j个数的方案数
dp[i][j]=dp[i-j][j]+dp[i-j][j-1]-dp[i-(m+1)][j-1(可从数形结合或者抽屉原理的方面来考虑)

问题四:求把n划分成k个奇数的方案数?

引用zhhx的:
可重复数字纯奇纯偶划分
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];
不可重复也是一样的。

例题:

洛谷P1025 数的划分

问题一“把n划分成k个正整数的方案数”的弱化版,好像是暴力dp就能过的那种,但可以拿来练手,判判边界之类的(水经验

#include <iostream>
#include <cstdio>
using namespace std;
int f[210][10];
int n,k;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)f[i][1]=1;
    for(int i=1;i<=k;i++)f[i][i]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=2;j<=k&&j<i;j++)
        {
            f[i][j]=f[i-1][j-1]+f[i-j][j];
        }
    }
    printf("%d",f[n][k]);
    return 0;
}

洛谷P4104 [HEOI2014]平衡

问题可转化为从1~2n+1中选出k个整数,使得这k个整数和为k*(n+1)
其实就是问题三“把n划分成k个不大于m的互不相同正整数的方案数”的模板
在原问题中应该是把k*(n+1)划分成k个不大于2n+1的互不相同的正整数的方案数
复杂度:O(n*k*k)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,k,p;
int f[100100*2][15];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
    memset(f,0,sizeof(f));
    scanf("%d%d%d",&n,&k,&p);
    for(int i=1;i<=(2*n+1);i++)f[i][1]=1;
    for(int i=2;i<=k*(n+1);i++)
    {
        for(int j=2;j<=k&&j<i;j++)
        {
            int m=2*n+1;
            if(i>(m+1))
            f[i][j]=f[i-j][j-1]%p+f[i-j][j]%p-f[i-(m+1)][j-1]%p;
            else f[i][j]=f[i-j][j-1]%p+f[i-j][j]%p;
        }
    }
    printf("%d
",(f[k*(n+1)][k]%p+p)%p);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Akaina/p/11259344.html