• 洛谷 P1025 数的划分 & LOJ #10018. 「一本通 1.3 例 1」数的划分


    CSDN同步

    洛谷原题链接

    ( ext{LOJ}) 原题链接

    愉快的三倍经验题。

    简要题意:

    给定 (n,k),求将 (n) 分为 (k) 个有序正整数之和 的方案数。

    (6 leq n leq 200 , 2 leq k leq 6).

    算法一

    搜索 + 剪枝。

    状态设计

    首先我们应当考虑,如何设计搜索状态。

    对本问题即以下的问题:

    • 如何保证和为 (n)?
    • 如何保证共 (k) 个数?
    • 如何保证有序?

    第一问,我们需要一个 sum,记录当前所选数的和。

    第二问,我们需要一个 p,记录当前所选数的个数。

    第三问,我们需要一个 ma,记录当前所选数的最大值(其实也即上次选的数)。

    那么,每次从 ma - sum 进行一个区间枚举即可。

    但是时间复杂度是 指数级 的,不得不说对 (n = 200 , k = 6) 的数据是可以通过的,因为方案数有限;但是数据一旦加强,后果不堪设想。

    剪枝

    以样例为例,(n=7,k=3).

    第一个数你会选 (3) 吗?显然不会,因为后面的数就算都取最小((3)) 也是 (3 imes 3 = 9 > 7) 了。

    所以,我们需要时刻保证 ma * (k-p+1)+sum<=n,即包括当前数在内 (k-p+1) 个数都选最小的 ma,最小的和如果超过 (n),说明无效。如果 (=n),计入答案。即 (geq n) 的情况都可以去除。这样快了很多!

    时间复杂度:(mathcal{O}( ext{wys})).

    实际得分:(100pts).

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
    
    inline void write(int x) {
    	if(x<0) {putchar('-');write(-x);return;}
    	if(x<10) {putchar(char(x%10+'0'));return;}
    	write(x/10);putchar(char(x%10+'0'));
    }
    
    int n,k,s=0;
    
    inline void dfs(int sum,int p,int ma) {
    //	printf("%d %d %d %d
    ",sum,p,ma,s);
    	if(p==k) {s+=(n-sum>=ma);return;}             // 最后一个数的选择剪枝
    	if(sum>=n) return;                            // 最后一个数还没选的情况下,和超过,剪枝       
    	if(ma*(k-p+1)+sum>n) return;
    	if(ma*(k-p+1)+sum==n) {s++;return;}           // 刚才说的两个剪枝
    	for(int i=ma;i<=n-sum;i++) dfs(sum+i,p+1,i);  // 往下一层走
    }
    
    int main() {
    	n=read(),k=read();
    	dfs(0,1,1);
    	write(s);
    	return 0;
    }
    
    
    

    (n=200 , k=6) 的情况这样的剪枝没有什么显著作用,本地测试该剪枝加与不加存在约 (1.5) 倍的差异。当然通过是肯定能通过的。

    于是本人本地测试了 (n = 200 , k = 10) 的数据。得到的结果:加剪枝,约 (55.4s);不加剪枝,约 (146.6s). 可以看到,在大数据的驱使下,剪枝的效率得到了超高的发挥,效率达到了约 (2.65) 倍!(当然,本人电脑极慢,( ext{CPU}) 内存分配给 ( ext{C++}) 的还是太少了)

    算法二

    考虑动态规划。

    易知,我们只要反手开一个记忆化,立刻复杂度变成 (mathcal{O}(nk)).

    代码略,在原搜索基础上修正即可。

  • 相关阅读:
    paip.oracle 10G 在WIN7安装总结
    paip.提升用户体验提取FLASH中图片
    paip.提升效率质量控制代码风格模板化
    paip.提升性能首页性能加快解决方案
    PAIP.提升安全性更好的签名HASH算法
    银联支付接口常见错误总结
    paip.提升用户体验WEB程序页面的手机及平板浏览器兼容支持
    paip.中国银联接口错误码总结
    paip.提升用户体验提高兼容性无JS支持总结
    paip. lbmall V3.1.1 乐彼多用户商城系统的安全漏洞总结
  • 原文地址:https://www.cnblogs.com/bifanwen/p/13445604.html
Copyright © 2020-2023  润新知