智乃买瓜(another version)

题目链接https://ac.nowcoder.com/acm/contest/23478/C

智乃来到水果摊前买瓜,水果摊上贩卖着若干个不同的西瓜,第i{i}i个西瓜的重量为wi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi/2。

也就是说对于每个西瓜,智乃都有三种不同的决策:

  1. 购买一整个重量为wi​的西瓜
  2. 把瓜劈开,购买半个重量为wi/2​​的西瓜
  3. 不进行购买操作

为了简化题目,我们保证所有瓜的重量都是一个正偶数。

现在智乃知道,购买西瓜的重量和分别为k=1,2,3...M时,购买西瓜的方案的种类数对10……9+7取余数后的结果。

她想要还原水果摊贩卖的这若干个不同的西瓜重量分别为多少,请你构造一个贩卖N个不同西瓜的水果摊,其购买西瓜的重量和满足智乃的要求,当出现有多种符合题目描述的答案时,你只用输出任意一种。

我们保证输入的数据至少存在一个N≤10^3的合法解。

输入描述:

第一行输入一个正整数M(1≤M≤10^3),表示智乃所知购买西瓜质量和的上限。

接下来一行M个整数,第i{i}i个整数表示购买西瓜的重量和为i时,购买西瓜的方案的种类数对10^9+7取余数后的结果。

输出描述:

首先输出一个整数N,西瓜的数目。要求你给出的N大小范围在][0,10^3]之间

接下来一行N个正偶数wi​,表示每个每个西瓜的重量。要求你给出的wi大小范围在[2,2×10^3]

输入数据保证,至少存在一组在输出范围内的合法解。

输入

6
1 2 1 3 2 3

输出

3
8 2 4

DP的逆向思维,怎么加回来就怎么减回去,代码中有详细解释

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
//智乃买瓜的逆版本,智乃买瓜为背包问题,方程f[i][j]=f[i-1][j]+f[i-1][j-w[i]]+f[i-1][j-w[i]/2]
using namespace std;
//此题显然逆向思维,考虑把每个物品从背包中拿出来
//将原方程移项可知f[i-1][j]=f[i][j]-f[i-1][j-w[i]]-f[i-1][j-w[i]/2]
//滚动数组优化后为f[j]=f[j]-f[j-w[i]]-f[j-w[i]/2] 
//即此物品不拿的状态可由这个方程得到 
typedef long long ll;
const int N = 1e3 + 5;
const int mod = 1e9 + 7;
ll f[N];
int ans[N];
 
int main() 
{
    int m, cnt = 0;
    scanf("%d", &m);
    f[0] = 1;
    for(int i = 1; i <= m; i++) 
        scanf("%lld", &f[i]);
    for(int i = 1; i <= m; i++) 
	{//原理为对于每个从小循环的i,如果f[i]存在,那他一定由一个i*2的瓜切一半得来 
	//那我们只需要将这个瓜对于f数组的影响都剪掉(通过上面的方程),那下一个存在的f[i]
	//就是由另一个i*2的瓜切一半得来 
        while(f[i]) 
		{
            ans[++cnt] = i * 2;
            for(int j = i; j <= m; j++) 
			{
                f[j] -= f[j - i];
                if(j >= i * 2) 
                    f[j] -= f[j - i * 2];
                while(f[j] < 0)
                	f[j] += mod;//因为上面两次做减法均可能使f[j]<0,所以要用while循环 
            }
        }
    }
    printf("%d\n", cnt);
    for(int i = 1; i <= cnt; i++) 
        printf("%d ", ans[i]);
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值