papamelon 305. 求和方案 Sumsets

https://www.papamelon.com/problem/305

给你一个数N,只能用2的幂次求和组成,问总共有多少种方案.

输入
包含多组测试数据,输入以EOF作为结束标志.
每组测试数据包含一个整数NN.
1≤N≤1,000,000
输出
输出一个整数. 由于结果可能会非常大,因此输出末尾的九位数.
样例 1
输入
7
输出
6

例子说明
7 =
1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+2+2
1+2+2+2
1+2+4
1+1+1+4

一种方法是观察找规律
要拆分为2的幂数字的组合, 那么要么包含1这个2^0的数。要么不包含。
如果能有1加入的2的幂数字组合该数字, 那么这些组合都可以一一对应 n-1的2的幂数字组合。
如果没有1的2的幂数字组合该数字,那么这些组合都可以意义对应 n/2的幂数字组合.
比如说 数字4的幂数字组合
1 如果组合包含有1数字, 那么从这些组合中减少一个1 就对应数字3的所有幂数字组合。
2 如果组合不包含1数字, 也就是说组合肯定都是偶数,这些组合的元素均除以2,对应的数字2的所有幂数字组合
因为幂数字组合只能分成包含1和不包含1的情况,所以上面两种情况 就覆盖了4的幂数字组合的所有情况。

数字4的幂数字组合有 4 2+2 2+1+1 1+1+1+1
按照上面的思路
2+2 4 对应数字2的幂数字组合 1+1 2, 他们的组合数目是一样的
1+1+1+1 2+1+1 对应数字3的幂数字组合 1+1+1 2+1 ,他们的组合数目是一样的

假设dp[x]表示x的2次幂数的组合方案数
那么dp[x]一部分可以由dp[x-1]转化而来
当x为偶数的时候 x的2次幂组合可以从 x/2的2次幂组合所有元素乘以2转化,而且和dp[x-1]的方案不重合
综上所述
当x为偶数的时候 dp[x] = dp[x/2]+dp[x-1]
当x为奇数的时候 dp[x]=dp[x-1]

#include <iostream>

using  namespace std;

const int N = 1000010;
long long dp[N];
int n;

void solve() {
	dp[0] = 0; dp[1] = 1; dp[2] = 2; dp[3] = 2; dp[4] = 4;

	for (int i = 5; i <= n; i++) {
		if (i % 2 == 0) {
			dp[i] = dp[i - 1] + dp[i / 2];
		}
		else {
			dp[i] = dp[i - 1];
		}
		dp[i] = dp[i] % 1000000000;
	}

	cout << dp[n] << endl;

}

int main()
{
	cin >> n;

	solve();

	return 0;
}

另一种解法从背包的角度来看待

dp[x][y]表示在可选取(2^0,2^1,...2^k)的多个元素下 组合成y的方案数
x=(2^0,2^1,...2^k) 每次 递增两倍
dp[x][y] = dp[x/2][y]+dp[x/2][y-x]+dp[x/2][y-2*x]+...+dp[x/2][y-z*x];

得到以下代码

// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

using  namespace std;

const int N = 1010 ;
int n;
long long dp[N][N];
const int MOD = 1000000000;


int main()
{
  cin >> n;
  int a = 1;
  for (int i = 1 ; i <= n; i <<= 1) {
    a = i;
    dp[i][0] = 1;
    for (int j = 1; j <= n; j++) {
      //一种方案  不选取i 全部由1~i/2的元素组成j
      dp[i][j] = dp[i>>1][j];
      if (j >= i) {
        dp[i][j] += dp[i][j - i];
      }
      dp[i][j] = dp[i][j] % MOD;
    }
  }  

  cout << dp[a][n] << endl;
}

上面的代码要想达到题目的范围 ,数组空间开的太大了。
所以需要优化空间。
优化成滚动数组,优化存储空间,代码如下

#include <iostream>

using  namespace std;

const int N = 1000010 ;
int n;
long long dp[2][N];
const int MOD = 1000000000;

int main()
{
	cin >> n;

	int a = 1;
	long long* prev = dp[0];
	long long * curr = dp[1];
	for (int i = 1 ; i <= n; i <<= 1) {
		a = i;
		curr[0] = 1;
		for (int j = 1; j <= n; j++) {
			curr[j] = prev[j];
			if (j >= i) {
				curr[j] += curr[j - i];
			}
			curr[j] = curr[j] % MOD;
		}
		swap(prev, curr);
	}

	cout << prev[n] << endl;
}

我的视频题解空间

posted on   itdef  阅读(49)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
历史上的今天:
2021-09-04 Leetcode 215. 数组中的第K个最大元素 排序 优先队列
2020-09-04 LeetCode 257. 二叉树的所有路径 dfs
2019-09-04 acwing 76. 和为S的连续正数序列
2019-09-04 leetcode 19 删除链表的倒数第N个节点
2019-09-04 水文一篇 汇报下最*的学**况
2018-09-04 muduo 的windows下的编译
2016-09-04 操作系统学习笔记(三) windows内存管理

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示