CF413C

正文

题意:

给 n 个关卡,每个关卡得分为 ai,有 m 次机会可以选择一
个关卡通过后不得分,而将现有得分翻倍

你可以安排关卡的通过顺序和策略,求最大得分.


分析:

看到这道题首先想到的就是贪心,贪心不仅是最好想也是最好写的。

  • 开始我们可以确定的是,如果有个关卡不能翻倍,那你只能把他的分值加上;
  • 另外,我们知道,如果基数越大,那他翻倍后的数就越大,
    这也是贪心的核心所在,如果我们想要更大的值,就要先得到更大的基数再把它翻倍,所以我们应先把所有只能加的数加起来在翻倍(因为题目给定分值不会为负数);
  • 还有,在遇到能翻倍的关卡的时候,也要判断一下到底是翻倍后得的分多还是不翻倍加上当前关卡分得的分多。

知道这些了以后,我们可以写代码了


做法

  • 我这里是一开始把所有关卡的分全都加上,再减去可以翻倍的关卡的分,作为开始的基数;
  • 然后新开一个数组专门存能翻倍的关卡的分,并从大到小排序,还是遵循了先基数大优先的准则,如果把大的放后面,那他能作为基数扩大后面的值的可能性就会变小,所以先放前面并每次比较加上它后的值与翻倍的值,取较大的那个;
  • 注意:我这里本来想用计数器记录翻倍关卡的数量,在排序时按照它的数量排,但是我这里赋值后关卡分值在数组里的分布是不定的,有的会排不到,所以要把所有的数组全排一遍。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring> 
#include<cmath>
#include<algorithm>
#define maxn 1000010

using namespace std;

long long n,m,a[maxn],b[maxn],c[maxn],cnt,ans;

int cmp(int x,int y){
	return x>y;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ans+=a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>b[i];
		for(int j=1;j<=n;j++){
		    if(b[i]==j){
		    	c[j]=a[b[i]];
		    	ans-=a[b[i]];
		    	cnt++;
		    } 
		}
	}
	sort(c+1,c+n+1,cmp);
	for(int i=1;i<=cnt;i++){
		if(ans*2>=ans+c[i]) ans*=2;
		else ans+=c[i];
	}
	cout<<ans;
	return 0;
} 

制作不易,若有错请各位指正

原文地址:https://www.cnblogs.com/KnightL/p/13935445.html