B - Balanced Diet(贪心+前缀和)

题目链接:Balanced Diet - Gym 102220B - Virtual Judge (vjudge.net)

泰勒在一家牛奶糖果店闲逛。这家商店有m种糖果,店里有n颗糖果。第i个颗的价值为ai,类型为bi。泰勒打算在店里买一些糖果,第i种糖果他至少买li个。Taylor知道均衡的饮食是很重要的,一个甜食集合的值被测量为S/C,其中S表示所选糖果价值的和,C表示在所有类型的甜食中同类型出现的最大次数。请编写一个程序帮助泰勒找到值最大的甜蜜集。


思路:我们需要开一个二维vector去存储每种类型糖果的每颗的价值,然后从大到小排,通过计算前缀和去实现选择该类型糖果多少颗的最大化(用g[ i ][ j ]代替),同时记录一下所有类型中最多的那种的数量ma。然后对结构体L中的LL进行从小到大的排序。

对于这个S/C这个式子最大的话要遍历每个S也就是选择1到ma的糖果数量

然后分母是所有的g[ i ][ j ]的加和(i为糖果的类型,j为选择糖果的数量,j要大于了L[ i ].LL,并且如果j > g[ i ].size()(也就是该类型糖果的数量),则 j = g[ i ].size()),然后计算每个S/C然后记录它最大时的S和C。最后输出最简形式。


代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n,m,x,y;
int a[N],b[N];
vector<int>g[N];
map<int,int>mp,mk;
struct node{
	int ll,id;//ll为该类型至少买多少颗,id为糖果类型
}l[N];
bool cmp(node a,node b){
	if(a.ll!=b.ll)
	return a.ll<b.ll;
	return a.id<b.id;
}

void solve () {
	cin>>n>>m;//输入糖果颗数n,糖果类型m
	double md=0;
	mp.clear();//清空mp容器
	for(int i=1;i<=m;i++)cin>>l[i].ll,l[i].id=i,g[i].clear();//输入第i类型至少买多少颗,同时清空g容器
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];//输入糖果价值以及对应的类型
		g[b[i]].push_back(a[i]);//将该价值放入对应类型的容器内
		
	}
	int ma=0;
	for(int i=1;i<=m;i++){
		sort(g[i].begin(),g[i].end(),greater<int>());//将同类型糖果价值从小到大排。
		for(int j=1;j<g[i].size();j++){
			g[i][j]+=g[i][j-1];//计算前缀和,使g[i][j]意思变为第i类型选择j颗糖果的最大价值
		}
		if(g[i].size()>ma)ma=g[i].size();//记录所有类型中同类型糖果最大数量
	}
	sort(l+1,l+m+1,cmp);int k=1;//将结构体按照至少买多少颗糖果从小到大排
	for(int i=1;i<=ma;i++){//遍历买1颗到买ma颗
		for(int j=1;j<=m;j++){//计算买i颗时最大价值
			if(l[j].ll>i){//如果没有到达买该糖果的最小需求则停止循环。
				break;
			}
			int d=g[l[j].id].size();
			if(min(i,d)-1<0)continue;//防止re因为有糖果有这个类型但没有数量。
			mp[i]+=g[l[j].id][min(i,d)-1];//min(i,d)是如果所选的i颗大于所有的数量就选所有的糖果
		}
		if(md<1.0*mp[i]/i){//求最大的S/C
			md=1.0*mp[i]/i;x=mp[i],y=i;//记录此时的S和C
		}
	}
	int c=gcd(x,y);//最大公因数
	cout<<x/c<<"/"<<y/c<<endl;//化为最简形式
	
}	
signed main () {
	int T = 1;
	std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>T;
	while (T --) solve ();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值