DP与贪心补题报告

1.自然数的拆分求方案数

时间限制:1秒        内存限制:128M

题目描述

  给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。注意:拆分方案不考虑顺序;至少拆分成2个数的和。   1≤N≤4000

求拆分的方案数 mod2147483648 的结果。

输入描述

  一个自然数N。  

输出描述

  输入一个整数,表示结果。  

样例

输入

7

输出

14

提示

1+1+1+1+1+1+1

1+1+1+1+1+2

1+1+1+1+3

1+1+1+2+2

1+1+1+4

1+1+2+3

1+1+5

1+2+2+2

1+2+4

1+3+3

1+6

2+2+3

2+5

3+4(共14行)

思路详解:

此题可想成完全背包问题,只需注意dp数组类型,不要为int整型,会爆(因为2147483648,INT_max)。

AC代码:

#include<iostream>
using namespace std;
const int m=2147483648ll;
int n;
long long dp[4005];
int main(){
	cin>>n;
	dp[0]=1;
	for (int i=1;i<n;i++){  //物品 
		for (int j=i;j<=n;j++){  //容量 
			dp[j]+=dp[j-i];
			dp[j]=dp[j]%m;
		}
	}
	cout<<dp[n];
	return 0;
}

2.自然数的拆分

时间限制:1秒        内存限制:128M

题目描述

任何一个大于1的自然数n,
总可以拆分成若干个小于n的自然数之和。
当n=7共14种拆分方法:
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14

输入描述

输入n。

输出描述

按字典序输出具体的方案。

样例

输入

7

输出

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

思路详解:

因为数据量小,所以用DFS不会爆,用temp存储,k为下标指示器,为减少时间,防止时间爆掉,可以从上一个数开始找。

AC代码:

#include<iostream>
using namespace std;
int n,temp[15]={1};
void dfs(int sum,int k){
	if (sum==0){
		cout<<temp[1];
		for (int i=2;i<k;i++){
			cout<<"+"<<temp[i];
		}
		cout<<endl;
	}
	for (int i=temp[k-1];i<n;i++){
		if (sum>=i){
			temp[k]=i;
			dfs(sum-i,k+1);
			temp[k]=0;
		}
	}
}
int main(){
	cin>>n;
	dfs(n,1);
	return 0;
}

3.货币系统

时间限制:1秒        内存限制:128M

题目描述

在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 a[i],你可以 假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n、面额数组为 a[1..n] 的货币系统记作 (n,a)。

在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对 每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i]× t[i] 的和为 x。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表 示出。例如在货币系统 n=3, a=[2,5,9] 中,金额 1,3 就无法被表示出来。

两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要 么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。他们希望你来协助完成这 个艰巨的任务:找到最小的 m。

输入描述

输入文件名为 money.in。

输入文件的第一行包含一个整数 T,表示数据的组数。接下来按照如下格式分别给 出 T 组数据。

每组数据的第一行包含一个正整数 n。接下来一行包含 n 个由空格隔开的正整数 a[i]。

输出描述

输出文件名为 money.out。 

输出文件共有 T 行,对于每组数据,输出一行一个正整数,表示所有与 (n,a) 等 价的货币系统 (m,b) 中,最小的 m。

样例

输入

2
4
3 19 10 6
5
11 29 13 19 17

输出

2
5

提示

在第一组数据中,货币系统(2,[3,10])和给出的货币系统(n,a)等价,并可以验证不存在m<2的等价的货币系统,依次答案为2.

在第二组数据中,可以验证不存在m<n,的等价的货币系统,因此答案为5.

对于 100% 的数据,满足 1 ≤ T ≤ 20,1≤n≤ 100,1≤ a[i] ≤ 25000。

思路详解:

多次输入记得初始化,使用动态规划判断哪些面额的货币是必要的。对货币面额进行排序,然后依次判断每个面额是否能被前面的面额组合表示出来。如果不能,则该面额是必要的。

AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n,dp[25005],a[105];
int cnt=0;
int main(){
	cin>>t;
	while (t--){
		cin>>n;
		cnt=0;
		memset(dp,0,sizeof(dp));
		for (int i=1;i<=n;i++){
			cin>>a[i];
		}
		sort(a+1,a+n+1);
		dp[0]=1;
		for (int i=1;i<=n;i++){  //物品
			for (int j=a[i];j<=a[n];j++){  //容量 
				dp[j]+=dp[j-a[i]]; 
			} 
		}
		for (int i=1;i<=n;i++){
			if (dp[a[i]]==1){
				cnt++;
			}
		}
		cout<<cnt<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值