刷题记录:牛客NC19158失衡天平

传送门:牛客

题目描述:

终于Alice走出了大魔王的陷阱,可是现在傻傻的她忘了带武器了,这可如何是好???这个时候,一个神秘
老人走到她面前答应无偿给她武器,但老人有个条件,需要将所选武器分别放在天平的两端,若天平平衡
则可以将天平上的所有武器拿走,还好这个天平锈迹斑斑,只要两端重量相差小于等于m就会保持平衡,
Alice傻傻的认为越重的武器越好,求Alice最多能拿走的武器总重量。(不限操作次数)
输入:
5 4
1 5 61 65 100
输出:
132

这道dp题感觉还是有点难度的,刚开始我并没有想出来(其实最后也没想出来),看了一遍题解之后恍然大悟(emmm,原来这就是dp题吗,我 t c l tcl tcl)

主要思路:

  1. 在推这道题的dp方程之前我们先来证明称多次天平和称一次天平其实是等价的,为我们后面做铺垫.我们假设 X 1 < X 2 , Y 1 < Y 2 , 且差值都在 m 之内 X1<X2,Y1<Y2,且差值都在m之内 X1<X2,Y1<Y2,且差值都在m之内,那么我们显然可以通过称两次来得到这四把武器,但是我们也可以这样 ( X 1 + Y 2 ) , ( X 2 + Y 1 ) (X1+Y2),(X2+Y1) (X1+Y2),(X2+Y1),此时我们会发现两者的差值显然也是在m以内的,所以我们就发现我们其实可以称一次来解决这个问题
  2. 我们分析一下题目之后(看了一下题解之后)发现我们可以使用 d p [ i ] [ j ] dp[i][j] dp[i][j]来记录前 i i i个武器天平左右两段相差 j j j的武器的总重量,那么显然的对于新枚举到的一把武器,我们有三种可能性,要么不放在天平中,此时我们的 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i1][j],假设我们此时放的位置加大了我们的差值,那么此时我们的 d p [ i ] [ j ] = d p [ i − 1 ] [ a b s ( j − a [ i ] ) ] + a [ i ] dp[i][j]=dp[i-1][abs(j-a[i])]+a[i] dp[i][j]=dp[i1][abs(ja[i])]+a[i]也就是将原来的差值继续加大,为什么要加上 a b s abs abs呢,是因为我们也可以加到质量原本较轻的位置,使其比较重的还要重.或者减少了我们的差值,此时 d p [ i ] [ j ] = d p [ i − 1 ] [ j + a [ i ] ] + a [ i ] dp[i][j]=dp[i-1][j+a[i]]+a[i] dp[i][j]=dp[i1][j+a[i]]+a[i],也就是将我们的差值减少到我们现在的位置
  3. 最后只要统计一下m以内的最大值即可

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int n,m;
int a[maxn];
int sum[maxn];int dp[200][100*100+5];
int main() {
	n=read();m=read();
	for(int i=1;i<=n;i++) {
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
	}
	memset(dp,-0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++) { 
		for(int j=0;j<=sum[i];j++) {
			dp[i][j]=dp[i-1][j];
			dp[i][j]=max(dp[i][j],dp[i-1][abs(j-a[i])]+a[i]);
			dp[i][j]=max(dp[i][j],dp[i-1][j+a[i]]+a[i]);
		}
	}
	int ans=0;
	for(int i=0;i<=m;i++) ans=max(ans,dp[n][i]);
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值