TopCoder SRM 691 Div1 500 Moneymanager

本文探讨了一种结合贪心策略与动态规划的DP算法新应用,通过对序列进行特定排序,利用交换相邻元素的贪心方法优化解决方案。详细介绍了如何通过预计算右侧元素和来简化计算过程,实现对特定问题的有效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习了DP的新姿势QAQ

首先应该不难发现如果无视XXX的话整个序列应该是按a[i]/b[i]a[i]/b[i]a[i]/b[i]从大到小排列的,这个就是个很基础的贪心吧,证明用的也是贪心中最常见的方法:交换相邻元素。

然后我们考虑一下这个XXX对答案的贡献其实就是X⋅∑i=n/2+1i=nb[i]X \cdot \sum_{i=n/2+1}^{i=n}b[i]Xi=n/2+1i=nb[i],并且XXX的两边肯定还是按a[i]/b[i]a[i]/b[i]a[i]/b[i]从大到小排列的。那么我们考虑dp,dp[i][j][k]dp[i][j][k]dp[i][j][k]表示前iii个元素,有jjj个放在左边,其余bbb的和为kkk的最大答案,然后会发现这样压根算不了贡献= =于是要在最开始就把右边bbb的和枚举出来,就可以直接算贡献了(这一步巨难想啊,感觉以前没碰到过类似的方法根本想不到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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值