牛客-4-I-爆炸的符卡洋洋洒洒

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
一道01背包题,他们说很简单QWQ,但窝想记录几个细节

题目

小红正在研究如何把符卡组合出尽可能大威力的组合魔法。

小红共有 n 种符卡可以选择,每种符卡最多只能选择一次,每个符卡的魔力消耗为ai​,威力为 bi​。
如果将多个符卡进行组合,则可以发动一个组合魔法。组合魔法的魔力消耗为选择的符卡的魔力消耗的总和,其威力为选择的符卡的威力的总和。
小红必须保证最终符卡的魔力消耗总和为 k 的倍数,否则小红将受到魔力反噬而发动魔法失败。
小红想知道,自己能发动的组合魔法最大的威力是多少?

输入描述:

第一行输入两个正整数 n和 k ,用空格隔开。

接下来的 n 行,每行输入两个正整数 ai 和 bi ,用空格隔开。
数据范围:
1≤n,k≤1000
1≤ai,bi≤10^9

输出描述:

如果小红无论如何也组合不了能发动的魔法,则输出-1。
否则输出最大的威力值。

示例1

输入

2 3
1 2
2 1

输出

3
说明
两个符卡都选上,融合出的魔法消耗为3,是3的倍数。威力是3。

示例2

输入

2 2
1 2
2 1

输出

1
说明
选择第二个符卡,消耗为2,是2的倍数。威力是1。

示例3

输入

3 4
1 2
5 3
1 4

输出

-1

说明

显然,无论如何都组合不出消耗为4的倍数的魔法。

思路

碰到  “ 选或不选 ”的问题,一般得往 背包 方向想,这题就是“选不选卡片”,“魔力消耗”相当于物品“体积”,“威力” 就相当于“价值”。

考虑 二维版 的话,发现ai, bi 都有1e^9那么大,开个dp[1000][1e9]貌似不太可行,再仔细看看这道题,k 只有1000,而我们只 关心 威力和  为k 的倍数的 ,而 k+1 , 2*k+1,  1000*k+1 ,它们都可以看作 等价的 ,这样,我们就可以将数组缩小到dp[1000][1000]级别了,只要将“体积”(即威力)模除K就行,状态转移方程:

  一种写法:f[i][j]= max( f[i-1][j] , f[i-1][((j-a)%k+k)%k] + b );

另一种写法:dp[i][ (j+a)%k] = max ( dp[i-1][ (j+a)%k)] , dp[i-1][j] + b );

所谓dp,就是由 已知的 推 未知的

max括号里前面的就是不选这个,后面的就是选,

注意第一种写法中 j-a 有可能会变负的,通过%K得到绝对值小于k的负的,再加k,再%k,就得到了正的余数了 (第二次见这种写法,多少有点亲切^ ^)

接下来  ,窝没有初始化 f 数组为负数 ,则测试样例通过率为0.....

为什么?

比如给一组数据(用题目示例3也行):

1  5

3  1

按理说要输出-1,而 f 不初始化为负的(全是0时),输出就是1.因为每次后面的选择都加了b,拿 f [0][0] ( 0 )和b比较,自然是b.但这是“不选”的情况,所以要使这种情况的后者为负的,才不会选中它,问题是,我用 int f[1005][1005] 只过了80%qwq,因为负得不够大,b有1e9,加个两次就正了....而当你开 long long,memset  -0x3f, 它就正不了了。或者一个for循环全初始化为-1e19也行 ,代码如下:

#include<iostream>
#include<cstring>
using namespace std;
long long n, k, f[1005][1005], a, b;
int i, j;
int main(){
    memset(f, -0x3f, sizeof(f));
    f[0][0]=0;
	cin >>n >>k;	
	for(i=1;i<=n;i++){
		cin >>a >>b;
		for(j=0;j<k;j++)
			f[i][j]= max(f[i-1][j], f[i-1][((j-a)%k+k)%k]+b);
	}
	if(f[n][0]<=0)cout <<-1;    //这里写if(f[n][0])也对,因为都由dp[0][0]转移过来,都>=0
	else cout <<f[n][0];
	return 0;
}

好一个简单的dp,好一个至弱的菜鸡(窝)QWQ

((还有一维优化版的,先不管了...

小伙伴们加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值