TopCoder SRM 691 Div1 500 Moneymanager

53 篇文章 0 订阅
27 篇文章 0 订阅

学习了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] Xi=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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值