hihocoder 编程练习赛76

目录

hihocoder 编程训练赛76

题目1 : 切割木棒

题意分析:

ac代码

题目2 : 取卡片游戏

题意分析:

        ac代码


hihocoder 编程训练赛76

题目1 : 切割木棒

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Hi有N根木棒,其中第i根的长度是Li。

现在小Hi会进行多轮切割,每轮行动中小Hi会选出其中最短的木棒,不妨设其长度为X。然后将所有长度为X的木棒移除。再将剩余的木棒都切掉X长度。如此反复直到木棒一根也不剩。

例如一开始木棒的长度是[2, 1, 3, 1],则第一轮之后1被移除,余下的又被切短为:[1, 2];第二轮之后余下的是[1];第三轮之后就一根也不剩了。

给定N根木棒,请你计算每轮切割过后,剩余木棒的长度之和是多少。

输入

第一行包含一个整数N。

第二行包含N个整数,L1, L2, L3, ... LN。

对于50%的数据,1 ≤ N ≤ 1000

对于100%的数据,1 ≤ N ≤ 100000 1 ≤ Li ≤ 1000000

输出

输出每轮之后剩余木棒的长度

样例输入

4
2 1 3 1

样例输出

3
1
0

 

题意分析:

1.题是什么?

    n个长度分别为ai的木棒,现在我们每次操作将所有木棒剪短当前最短的木棒的长度,剪为0视为这根木棒已经没有了,

    也就是如果现有木棒4根分别为4,4,6,8;

    第一次剪短将每根木棒剪短4,剩余两根木棒长度为2,4,

    第二次剪短2,剩余一根木棒2,

    第三次剪短2,剩余0根木棒,

我们要输出的答案就是每次被剪短之后剩余的所有木棒的总长度.

2.思路

    最近刚做了一个计数排序的博客,因为每次剪都是剪最短的并且木棒最长1000000,刚好能开数组做计数,故而我首先想到把这所有木棒用一个1000000大小的数组做计数存储,然后从0到1000000的遍历,每遍历到一个存在值的点就进行一遍剪操作,而最后的答案到底该加多少则由逆向思维处理,先把所有木棒总长度叠加处理,然后每次操作剪掉了多少就减去多少,剩下的就是本次剪完的总长度了;

ac代码

#include <stdio.h>
typedef long long ll; 
const int maxm=1000001;
int count[maxm];
int main(){
	ll n;
	scanf("%lld",&n);
	for(int i=0;i<maxm;i++) count[i]=0;
	ll t,sum=0;//sum表示当前木棒总长度 
	for(int i=0;i<n;i++){
		scanf("%lld",&t);
		count[t]++;
		sum+=t;
	}
	ll num=n; //当前总共多少个木棒 
	ll ans[n],ansnum=0,nowreduce=0; //nowreduce表示这些木棒曾被削断多少 
	for(int i=0;i<maxm;i++){
		if(count[i]){
			sum-=(i-nowreduce)*num; 
			ans[ansnum++]=sum;
			nowreduce+=(i-nowreduce);
			num-=count[i];
		}
	}
	for(int i=0;i<ansnum;i++) printf("%lld\n",ans[i]);
	return 0;
}

题目2 : 取卡片游戏

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Hi和小Ho在玩一种游戏。他们面前有N张卡片,每张卡片上都写有一个整数,依次是A1, A2, ... AN。  

他们可以轮流从中取卡片。每次可以取出1、2或3张卡片(不能不取),取走的卡片不能被再次拿取,直到最后一张卡片被取走。

这时小Hi和小Ho各自取走的卡片上的整数之和就是他们的得分。  

假设小Hi和小Ho都足够聪明,都会选择使自己得分最高的策略。请你计算先手的小Hi最多能得几分。

输入

第一行包含一个整数N。  

第二行包含N个整数A1, A2, A3, ... AN。  

1 ≤ N ≤ 100000

-1000000 ≤ Ai ≤ 1000000

输出

输出小Hi最多的得分

样例输入

3  
-1 1 2

样例输出

3

题意分析:

1.题是什么

    相当于给你和他n个数字,取值范围为-1000000到1000000,现在你们每次可以拿走三个或两个或一个数字,随便拿哪个,你先拿,问你你最大能拿到和为多少.

2.思路

    一个明显的博弈论问题,首先就该想到dp(虽然我当时脑袋抽了没想到),想到dp自然该确定dp[i]的意义以及递推公式,而我设计的这个dp中dp[i]存的是在抽取完前n张卡牌之后先手抽取的人所能达到的最大分值,先对这n个数字排序是自然的,然后

递推公式为:dp[i]=std::max(sum[i-1]-dp[i-1]+a[i],std::max(sum[i-2]-dp[i-2]+a[i]+a[i-1],sum[i-3]-dp[i-3]+a[i]+a[i-1]+a[i-2]));

    因为是两个人轮流拿,故而我做了一个sum数组,sum[i]表示前i张包括第i张的和为多少,这样sum[i-1]-dp[i-1]就能知道那一次选择我最优能拿到多少了,毕竟dp[i-1]存的是另一个人的最优选择.故而dp[i]自然就是拿一张两张和三张之中的最优答案,取最大值就好.

ac代码

#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;

typedef long long ll; 
const int maxn=100001;
const int maxm=1000001;
ll a[maxn];
ll dp[maxn]; //dp[i]存的是在抽取完前n张卡牌之后先手抽取的人所能达到的最大分值 
ll sum[maxn];

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	sort(a+1,a+n+1);
	for(int i=0;i<=n;i++) dp[i]=0;
	sum[0]=0;
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	dp[0]=0;
	dp[1]=a[1];
	dp[2]=a[2];
	dp[3]=std::max(a[2]+a[3],a[1]+a[2]+a[3]);
	for(int i=4;i<=n;i++){
		dp[i]=std::max(sum[i-1]-dp[i-1]+a[i],std::max(sum[i-2]-dp[i-2]+a[i]+a[i-1],sum[i-3]-dp[i-3]+a[i]+a[i-1]+a[i-2]));
	}
	printf("%lld\n",dp[n]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值