51nod 1259 “整数划分 V2“ 动规

ACM 同时被 2 个专栏收录
7 篇文章 0 订阅
2 篇文章 0 订阅

1259 “整数划分 V2” 动规

1259 整数划分 V2

题面比较直白, 不解释

这个整数划分和dp的入门题的整数划分有不同,就是数据范围, 这个数据范围是5w, O(n^2)肯定不行.

这里的方法是我们想象: 把N的划分分为两个部分: 成分为**[1, 根号n], 另一部分是[(根号n) + 1, n]**.

比如n = 4: 4的开方为2; 那么我们对于{1, 3}, 这个划分, 就是由{1}, {3} 组成的,那么我们发现, 如果我们考虑小于根号n的这个部分, 我们可以设置状态dp[ n ] [根号n], 那么dp[i] [j]就表示为总和为i, 其中的最大的划分成分为j时候的可行的划分种数: 这个与入门题的整数划分思想一致,可以通过两次遍历求出这个数组中的所有值

那么对于第二部分:即每一个成分都大于根号n, 我们不难看出, 如果对于一个数i, 划分的结果中最小的成分不小于"(根号n)+1",那么每一个划分的组成元素的数量不会超过根号n, 想象总和为10000, 根号n就是100, 那么100个100就可以组成10000, 所以成分最多不会超过根号n个, 那么我们就可以把成分的数量设为dp的状态 dp1[ n ] [根号n] , 那么dp1[i] [j]表示总和为i, 由j个成分比根号n大的元素组成的划分的数量, 这个转移就可以考虑为: 假设当前dp1[sum] [j], 那么我要求最小的元素必须不小于"(根号n)+1", 所以 ①由dp1[sum- 根号n - 1] [j - 1]可以得到, 这个转移的原理就是我在dp1[sum- 根号n - 1] [j - 1]的所有划分上加一个单独的元素-------"(根号n)+1" ②由dp1[sum- j] [j]得到, 原理就是我在dp1[sum- j] [j]的所有划分上对于每一个元组的值都加上"1", 也是符合题意的.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 60;
const int mod = 1e9 + 7;

//全部由m以及它以下的成分组成
ll dp1[maxn];

//全部由m以上的成分组成
ll dp2[maxn][250];
ll sum[maxn];

void solve(){
   int n;
   cin>>n;
   int m = sqrt(n);
   dp1[0] = 1;
   //m是允许的最大的值
   for(int maxval = 1; maxval <= m; maxval++){
       for(int tot = maxval;tot <=n;tot++){
           dp1[tot] += dp1[tot - maxval];
           dp1[tot] %= mod;
       }
   }
   
   dp2[0][0] = 1;
   //sum表示用大于(根号n)+1 的元素组成的总的方案数
   sum[0] = 1;
   //注意tot的起始值
   for(int tot = m + 1; tot <= n;tot++){
       for(int chose = 1;chose <= m;chose++){
           dp2[tot][chose] = dp2[tot - (m + 1)][chose - 1] + dp2[tot - chose][chose];
           dp2[tot][chose]%=mod;
           sum[tot]+=dp2[tot][chose];
           sum[tot]%=mod;
       }
   }
   ll ans = 0;
   for(int i = 0; i <= n; i++){
       ans+=dp1[i] * sum[n-i];
       ans%=mod;
   }
   cout<<ans<<endl;
}
int main() {
   //freopen("in.txt", "r", stdin);
   int T;
   solve();
}
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

Π鱼星先生

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值