学习了DP的新姿势QAQ
首先应该不难发现如果无视 X X X的话整个序列应该是按 a [ i ] / b [ i ] a[i]/b[i] a[i]/b[i]从大到小排列的,这个就是个很基础的贪心吧,证明用的也是贪心中最常见的方法:交换相邻元素。
然后我们考虑一下这个 X X X对答案的贡献其实就是 X ⋅ ∑ i = n / 2 + 1 i = n b [ i ] X \cdot \sum_{i=n/2+1}^{i=n}b[i] X⋅∑i=n/2+1i=nb[i],并且 X X X的两边肯定还是按 a [ i ] / b [ i ] a[i]/b[i] a[i]/b[i]从大到小排列的。那么我们考虑dp, d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示前 i i i个元素,有 j j j个放在左边,其余 b b b的和为 k k k的最大答案,然后会发现这样压根算不了贡献= =于是要在最开始就把右边 b b b的和枚举出来,就可以直接算贡献了(这一步巨难想啊,感觉以前没碰到过类似的方法根本想不到QAQ)。
#include <bits/stdc++.h>
#define ll long long
#define fr(i,x,y) for(int i=x;i<=y;i++)
#define rf(i,x,y) for(int i=x;i>=y;i--)
#define frl(i,x,y) for(int i=x;i<y;i++)
using namespace std;
const int N=51;
struct data{
int a,b;
}c[N];
int n,m;
int f[N][N][550];
template<class T> void checkmin(T &a,const T &b) { if (b<a) a=b; }
template<class T> void checkmax(T &a,const T &b) { if (b>a) a=b; }
class Moneymanager {
public:
int getbest( vector <int> a, vector <int> b, int X ) ;
};
int cmp(const data &q,const data &w){
return q.a*w.b>w.a*q.b;
}
int dp(int s,int X){
memset(f,-1,sizeof f);
f[0][0][0]=s*X;
int preb=0,sb=0;
fr(i,1,n) sb+=c[i].b;
frl(i,0,n){
preb+=c[i].b;
fr(j,0,m)
fr(k,0,i*10)
if (f[i][j][k]!=-1){
int a=c[i+1].a,b=c[i+1].b;
checkmax(f[i+1][j+1][k],f[i][j][k]+(sb-(preb-k))*a);
checkmax(f[i+1][j][k+b],f[i][j][k]+(s-k)*a);
}
}
return f[n][m][s];
}
int Moneymanager::getbest(vector <int> a, vector <int> b, int X) {
n=a.size();
fr(i,1,n) c[i]=(data){a[i-1],b[i-1]};
sort(c+1,c+1+n,cmp);
m=n/2;
int ans=-1;
fr(i,1,m*10) ans=max(ans,dp(i,X));
return ans;
}