失衡天平(DP)
失衡天平
https://ac.nowcoder.com/acm/problem/19158
题目描述:
有一个长度为n的序列,从中选择任意个数字将其分成两堆使两堆的和最大(且两堆的差值不超过m)
先证明一下 取两次转化成一次的正确性
假设
第一次取了 a ,b
第二次取了 c ,d
a-b=m
c-d=m-1
那么如果我们把c放在a这端 明显是错误的
我们把c放在b端
( a+d )- (c+b)=-(m-1)+m=1
也就是说我们所有的操作都可以转化为一次实现
这里我们考虑dp的状态表示
设dp[i][j]表示前i个数中 差值为j 的时候能够带走的武器的最大重量 对于每种武器,我们有三种选择 1.不选 dp[i][j]=max(dp[i][j],dp[i-1][j]); 2.放在重量大的一端 dp[i][j]=max(dp[i][j],dp[i-1][abs(j-a[i])]+a[i]); 3.放在重量小的一端 dp[i][j]=max(dp[i][j],dp[i-1][j+a[i]]+a[i]); 属性 max 答案 for(int i=0 ; i<=m ; i++) ans=max(dp[n][i],ans);
代码
#include <map> #include <set> #include <cmath> #include <stack> #include <queue> #include <cstdio> #include <bitset> #include <vector> #include <iomanip> #include <sstream> #include <cstring> #include <iostream> #include <algorithm> #include <unordered_map> #define UpMing main #define re register #pragma GCC optimize(2) #define Accept return 0; #define lowbit(x) ((x)&(-(x))) #define mst(x, a) memset( x,a,sizeof(x) ) #define rep(i,a,b) for(int i=(a);i<=(b);++i) #define dep(i,a,b) for(int i=(a);i>=(b);--i) using namespace std; typedef long long ll; typedef pair<int,int> PII; typedef unsigned long long ull; const int inf =0x3f3f3f3f; const int maxn=1e5+7; const ll mod = 1e9+7; const int N =1e6+3; inline ll read() { ll x=0; bool f=0; char ch=getchar(); while (ch<'0'||'9'<ch) f|=ch=='-', ch=getchar(); while ('0'<=ch && ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } void out(ll x) { int stackk[20]; if(x<0) { putchar('-'); x=-x; } if(!x) { putchar('0'); return; } int top=0; while(x) stackk[++top]=x%10,x/=10; while(top) putchar(stackk[top--]+'0'); } ll n,m,a[121],vis[121],ans,dp[121][12015]; int UpMing() { cin>>n>>m; for(int i=1 ; i<=n ; i++) a[i]=read(); mst(dp,-inf); dp[0][0]=0; for(int i=1 ; i<=n ; i++) { for(int j=0; j<=12012; j++) { dp[i][j]=max(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]); } } for(int i=0 ; i<=m ; i++) ans=max(dp[n][i],ans); out(ans); Accept; }