完美笔试

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

#define pb push_back
#define mp make_pair
#define ll long long
#define ull unsigned ll
#define db double
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define PII pair<int, int>

int main() {
	int N;
	ll a;
	cin >> N;
	priority_queue<ll, std::vector<ll>, std::greater<ll>> pq;
	for (int i = 0; i < N; i++) {
		cin >> a;
		pq.push(a);
	}
	ll ans = 0;
	while (pq.size() >= 2) {
		ll a = pq.top();
		pq.pop();
		ll b = pq.top();
		pq.pop();
		ans += (a + b);
		pq.push(a + b);
	}
	cout << ans << endl;
}



题目描述:
在主城站街很久之后,小萌决定不能就这样的浪费时间虚度青春,他打算去打副本。
这次的副本只有一个BOSS,而且BOSS是不需要击杀的,只需要和它比智力…….
BOSS会列出一正整数的序列,由小萌先开始,然后两个人轮流从序列的任意一端取数,取得的数累加到积分里,当所有数都取完,游戏结束。
假设小萌和BOSS都很聪明,两个人取数的方法都是最优策略,问最后两人得分各是多少。
输入
第一行:一个正整数N(2 ≤ N ≤ 100),表示序列中正整数的个数。
第二行至末尾:用空格隔开的N个正整数(1 ≤ a[i] ≤ 200)
输出
只有一行,用空格隔开的两个数,小萌的得分和BOSS的得分。
样例输入
6
4 7 2 9 5 2
样例输出

18 11

思路

一、取数游戏      

       双人游戏,N(2<=N<=100)个正整数序列放在一个桌子上,两人轮流从序列的两端的任意一端取数。

Alice 和Bob 轮流取数,Alice先取,两人都足够聪明,问最后两人的最高的得分是多少?

思路s[M][M]保存先手先拿和后拿能取得的最优值;

       如果先拿,可以取这个数,而且是拿左边、拿右边两者中最优的一个。

       如果后拿,对方回拿左边和拿右边两者中最优的一个,那么自然留下来的就最少

代码

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
    1. #define M 105  
    2. int a[M],n;  
    3. int s[M][M];  
    4. int dfs(int l,int r,int who)  
    5. {  
    6.     if(s[l][r])return s[l][r];  
    7.     if(l==r&&!who){s[l][r]=a[l];return a[l];}  
    8.     if(l==r&& who){s[l][r]=0;return 0;}  
    9.     //先拿  
    10.     if(!who) return s[l][r]=max(dfs(l+1,r,!who)+a[l],dfs(l,r-1,!who)+a[r]);  
    11.     else  return s[l][r]=min(dfs(l+1,r,!who),dfs(l,r-1,!who));  
    12. }  
    13. int main()  
    14. {  
    15.     while(~scanf("%d",&n))  
    16.     {  
    17.         for(int i=0;i<n;i++)  
    18.             scanf("%d",a+i);  
    19.         CLS(s,0);  
    20.         int ans=dfs(0,n-1,0);  
    21.         printf("%d %d\n",ans,accumulate(a,a+n,-ans));  
    22.     }  
    23.     return 0;  
    24. }  

二、签到积分

Description

经过一段时期的观察,老师觉得ACM实验室的利用率太低了,于是决定雇用老套的签到积分制度,但考虑到ACMer的成长必须持之以恒,因此,专门设计了一种激励连续签到的规则。
签到积分规则如下:
1) 每天只要来到ACM实验室学习并录入指纹,即算签到成功,可获取相应的积分;若某天未签到,则当天计0个积分。
2) 连续签到时,第1天签到可获取1个积分,第2天签到可获取2个积分,……,第n天可获取n个积分。
3) 如果中途未签到,则再签到时,连续天数重新从1开始计算。

自从有了积分制度后,ACM实验室的利用率大大的提高。
有一天集训队队长在签到后向大家出了一个难题,她向大家亮出了自己的签到积分总和,然后要大家算出她至少要签到多少天,并算出从开始签到到最后签到之间至少有多少天未签到。

Input

有多组测试数据
每行输入1个一个正整数n(1<=n<=170000)

Output

与输入的测试数据相对应,每行输出2个数据,一个是签到的最少天数,一个是中途未签到的最少天数,2个数用空格隔开。

思路有600件商品,体积为v[i],每件花费为i,用最少的花费装满背包(=最少签到天数)
体积为v[i],每件花费为1,用最少的花费装满背包(=中途未签到的最少天数+1)
每件物品无数多件,那就是一个完全背包问题(上面所求的两个子问题
没有依赖关系

 如果有依赖关系,比如:n=33,33=28+3+1+1  最少签到天数是11(7+2+1+1),中途未签到最少次数为3,

33=21 6+6  最少签到天数是12 (6+3+3),中途未签到最少次数为2

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #define M 170005  
  2. int v[600]= {0};  
  3. int day[M]= {0},cost[M]= {0};  
  4. void init()  
  5. {  
  6.     for(int i=1; i<600; i++) v[i]=v[i-1]+i;  
  7.     for(int i=1; i<M; i++)  day[i]=cost[i]=INF;  
  8.     for(int i=1; i<600; i++)  
  9.         for(int j=v[i]; j<M; j++)  
  10.         {  
  11.             day[j]=min(day[j-v[i]]+i,day[j]);  
  12.             cost[j]=min(cost[j-v[i]]+1,cost[j]);  
  13.         }  
  14. }  
  15. int main()  
  16. {  
  17.     init();int n;  
  18.     while(~scanf("%d",&n))  
  19.     {  
  20.         printf("%d %d\n",day[n],cost[n]-1);  
  21.     }  
  22.     return 0;  
  23. }  



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值