2021.09.16做题记录 洛谷P2690摘苹果/P6065Sumsets

题目描述
很少有人知道奶牛爱吃苹果。农夫约翰的农场上有两棵苹果树(编号为1和2),
每一棵树上都长满了苹果。奶牛贝茜无法摘下树上的苹果,所以她只能等待苹果
从树上落下。但是,由于苹果掉到地上会摔烂,贝茜必须在半空中接住苹果(没有人爱吃摔烂的苹果)。贝茜吃东西很快,她接到苹果后仅用几秒钟就能吃完。每一分钟,两棵苹果树其中的一棵会掉落一个苹果。贝茜已经过了足够的训练,
只要站在树下就一定能接住这棵树上掉落的苹果。同时,贝茜能够在两棵树之间
快速移动(移动时间远少于1分钟),因此当苹果掉落时,她必定站在两棵树其中的一棵下面。此外,奶牛不愿意不停地往返于两棵树之间,因此会错过一些苹果。苹果每分钟掉落一个,共T(1<=T<=1000)分钟,贝茜最多愿意移动W(1<=W<=30)
次。现给出每分钟掉落苹果的树的编号,要求判定贝茜能够接住的最多苹果数。 开始时贝茜在1号树下。
输入格式
第一行2个数,T和W。接下来的t行,每行一个数,代表在时刻t苹果是从1号苹果树还是从2号苹果树上掉下来的。
输出格式
对于每个测试点,输出一行,一个数,为奶牛最多接到的苹果的数量。
样例输入
7 2
2
1
1
2
2
1
1
样例输出  6

可以看出这题是DP,可以结合奇偶性判断进行某次移动是否能

我原本是定义dp[i][j]为前i分钟移动不超过 j次的最大值然后就想出了如下方程:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+1),进行当前移动能接到当前苹果时
dp[i][j]=max(dp[i-1][j]+1,dp[i-1][j-1]),进行当前移动不能接到当前苹果时

但这是错误的。考虑给出的样例,以上递推式可以正确求出dp[1][1]=1,但却错误地求出dp[2][1]=2(实际为1),这是因为:在前2分钟进行不大于1次移动能接到苹果的必要条件是第1、2分钟前都没有移动,而在前1分钟进行不大于1次移动的必要条件是第1分钟前进行了移动,这就导致了矛盾。

我们可以定义三维数组dp[i][j][k],最后一维表示在哪个苹果树下。这样就可以根据a[i]的值做出选择k=1还是=2的判断了。但还有更好的方法。

定义dp[i][j]为前i分钟恰好移动j次的最大值。那么最终答案就在dp[T] 中产生(注意不一定是dp[T][W]),在网上找到了如下递推式:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]),(当进行当前移动能接到当前苹果时还要再加1)。
这个式子我虽然理解了,但没有完全理解。以后想到再补充。
可以结合奇偶性压缩空间,最终代码长下面的样子。

#include <cstdio>
#include <algorithm>
const int M=1001,MAXW=31;
using namespace std;
int dp[2][MAXW];
//第二维是移动次数,不是最多移动次数
char a[M];
inline bool check(int i,int j) {
	return (j&1)?a[i]==2:a[i]==1;
}

int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)	   scanf("%hhd",&a[i]);
    for(int i=1; i<=n; i++) {
    	dp[i&1][0]=dp[(i-1)&1][0];
    	if(a[i]==1)    dp[i&1][0]++;
    	for(int j=1; j<=i&&j<=m; j++) {
    		dp[i&1][j]=max(dp[(i-1)&1][j],dp[(i-1)&1][j-1]);
    		if(check(i,j)) {  //移动将得到当前苹果
    			dp[i&1][j]++;
			}
		}
	}
	n&=1;
	printf("%d",*max_element(dp[n],dp[n]+m+1));
    return 0;
}

题目描述
给出一个整数 N,将 N分解为若干个2的次幂的和,共有多少种方法?
输入格式
输入一个整数 N( 1 ≤ n ≤ 1 0 6 1\leq n\leq 10^6 1n106)。
输出格式
输出方案数对 1 0 9 10^9 109 取模的结果。

这题我是用完全背包做的,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。其实可以轻松地找到递推关系:
f(1)=1,除了1之外,所有n为奇数时:f(n)=f(n−1),这是因为只要在某个偶数的所有分解方案中加上1就是它下一个奇数的所有分解方案,不会产生新方案。
当n为偶数时:f(n)=f(n−1)+f(n/2),这样就能很轻易写出代码了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值