武理校赛A题 ljw的剥削(思维 + map应用)

武理校赛A题

ljw的剥削(思维 + map应用)

牛客链接

题意:

给定 a[],b[] 两个长度同为 n 的数组,经过一系列操作后,
使 p = ∑ i = 1 n m a x ( ( a i − b i ) , 0 ) p=\sum_{i=1}^nmax((a_i-b_i),0) p=i=1nmax((aibi),0) 最小。 (0 < i <= n)

有两种操作:
1:选择一个值 x,所有 b 数组中等于 x 的值全部变为原来的两倍
if (b[i] == x) b[i] * =2
2:选择一个值 x,所有 b 数组中等于 x 的值全部加上前一个值
if (b[i] == x) b[i] += b[i - 1], 记b[0] = 0

共有10次操作机会,不同的操作不能选择相同的 x,(即若在操作1中读入 x 0 x_0 x0,在操作2中就不允许读入 x 0 x_0 x0

同一种操作可以选择相同的 x,但是不论选了几次 x,都只执行一次

注意输出格式 巨麻烦不想写了直接复制

输出10行,每行两个数字,两个值之间用空格隔开,第一个数字为1或者2,1表示释放“我爱ljw”技能,2表示释放“ljw爱上我”技能,第二个数字表示该技能选择的非负整数。10行输出按照第二个数字从小到大的顺序输出。如果有多种满足条件的结果,请输出选择的10个非负整数的和最小的方案,若有多种满足条件的结果中10个非负整数的和相同,但选择的技能不同的方案,则输出10个技能的数字和(即第一个数字的和)最小的方案。

思路:

(x 为选择的值)
维护两个map,记录当 x == b[i] 时,选择操作1(或2)能够使整个式子的值减小多少。

map[key] = value 代表当选择 key 作为 x 时,能使整个式子的值减小 value 。

然后将 mp1 和 mp2 的合并排序,取出前10个 key 和标签再输出即可(若不足10个,只需先输出若干行 1 0)

排序按照 map 的 value 大的优先,若value相等,则mp1的值排在mp2前面。

注意:取出的十个 key 中不能有相同的值(即 mp1 和 mp2 中存在相同的key,只取排在前面的那一个 key)

细节:

遍历a[i],当a[i] > b[i]时,让两个map进行如下操作:

mp1[b[i]] += min(a[i] - b[i], b[i]);//原本受到a[i] - b[i]点伤害; 当a[i] > 2 * b[i] 时,减少b[i]点伤害,否则减少a[i] - b[i]点伤害(即不受伤)
mp2[b[i]] += min(a[i] - b[i], b[i - 1]);//同理

注意到数据较大(1e9),所以可以将 vis 数组改为 map<int, int> vis ,记录这个值是否选择过

完整代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<math.h>
#include<queue>
using namespace std;
typedef long long ll;
typedef double dd;
typedef pair<int, int> pii;
typedef pair<dd, dd> pdd;
const int MAXN = 200010;
const int inf = 1e9 + 7;
int n, cnt;
int a[MAXN], b[MAXN];
map<int, int>vis; //开成数组结果 RE 了七八次,选择开成map

struct TY {
	int num, val, id;//num: 操作 1 或 2,val: 使整个式子减小多少,id: 选择的 x 的值
	bool operator<(const TY& a) {
		if (val == a.val) return num < a.num;
		return val > a.val;
	}
}e[MAXN << 1], g[MAXN << 1];

map<int, int>mp1, mp2;

bool cmp(TY a, TY b)
{
	return a.id < b.id;
}

int main()
{
	//freopen("D:\\C\\acm\\2021\\ljwzbj0.in", "r", stdin);
	//输入
	int n;
	scanf("%d", &n);
	for (int i = 1;i <= n;i++) scanf("%d", &a[i]);
	for (int i = 1;i <= n;i++) scanf("%d", &b[i]);
	//遍历,操作
	for (int i = 1;i <= n;i++)
	{
		if (a[i] <= b[i]) continue;
		mp1[b[i]] += min(a[i] - b[i], b[i]);
		mp2[b[i]] += min(a[i] - b[i], b[i - 1]);
	}
	//合并两个map
	int cnt = 0;
	for (auto& p : mp1) e[++cnt] = { 1,p.second,p.first };
	for (auto& p : mp2) e[++cnt] = { 2,p.second,p.first };
	sort(e + 1, e + 1 + cnt);
	//去掉重复选择的值,g[]存最终答案
	int cnt2 = 0;
	for (int i = 1;i <= cnt;i++)
	{
		if (vis[e[i].id] == 1) continue;
		g[++cnt2] = e[i];
		vis[e[i].id] = 1;
	}
	//小于10个的话输出若干个 1 0
	if (cnt2 < 10)
	{
		sort(g + 1, g + 1 + cnt2, cmp);//排序,符合输出格式
		for (int i = 1;i + cnt2 <= 10;i++)
		{
			printf("1 0\n");
		}
		for (int i = 1;i <= cnt2;i++)
		{
			printf("%d %d\n", g[i].num, g[i].id);
		}
	}
	//大于10个输出前十个
	else
	{
		sort(g + 1, g + 11, cmp);
		for (int i = 1;i <= 10;i++)
		{
			printf("%d %d\n", g[i].num, g[i].id);
		}
	}
}

总结:

比赛时看到此题题面长,表述复杂,队友也说是个毒瘤题,导致没有过多关注,赛后来看此题,思路不难,主要复杂在读懂题意,输出格式。

码完之后 RE 了七八发,最后发现是一开始 vis 开成了数组 vis[200010],改成 map 之后过了。

总的来说,比赛没做出来有点可惜。看来还是要加强阅读理解能力

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值