• 51nod 1380:夹克老爷的逢三抽一


    基准时间限制:1 秒 空间限制:131072 KB 分值: 320 难度:7级算法题
     收藏
     取消关注
    又到了诺德县的百姓孝敬夹克大老爷的日子,带着数量不等的铜板的村民准时聚集到了村口。
    夹克老爷是一位很"善良"的老爷,为了体现他的仁慈,有一套特别的收钱的技巧。
    1、让所有的村民排成一队,然后首尾相接排成一个圈。
    2、选择一位村民收下他的铜钱,然后放过他左右两边的村民。
    3、让上述三位村民离开队伍,并让左右两边的其他村民合拢起来继续围成一个圈。
    4、重复执行2、3直到村民全部离开。
    夹克老爷的家丁早早的组织村民排成一队并清点了村民人数和他们手里的铜钱数量。
    作为夹克老爷的首席师爷,你要负责按照夹克老爷的收钱技巧完成纳贡的任务。
    聪明的你当然知道夹克老爷并不像他表现出来的那样仁慈,能否收到最多的钱财决定了你是否能够继续坐稳首席师爷的位置。
    今年村民的人数是N,恰巧是3的倍数。


    提示:第2步选择村民时不需要按照任何顺序,你可以选择任何一位仍然在队伍里的村民收取他手中的钱财并放走他两侧的村民(这就意味着你无法同时收取到这两位的铜钱了)
    Input
    第一行1个整数N(3 <= N <= 10^5 - 1, N % 3 == 0)
    第2 - N + 1行:每行1个数对应村民i手中的铜钱。(0 <= m[i] <= 10^9)
    Output
    一个整数,说明在夹克老爷的收钱规则下你最多能够为夹克老爷搜刮到多少铜钱
    Input示例
    6
    6
    2
    3
    4
    5
    9
    Output示例
    13

    问题等价于N长的数组中抽取N/3个不相邻的值使得和最大(首尾也不能同时取)

    普通的dp方法复杂度n^2会TLE
    用贪心,类似最大M子段和的方式
    初始全部数字可选状态,进优先队列,弹出最大值,结果中加上选中的最大值,删除最大值左右两边的值,将最大值位置的值修改为左右两边的和减去中间的数重新进入优先队列,循环处理直到拿到要求的数据个数。

    以上是官方题解。。。头一次用优先队列做这种题,结果中加上选中的最大值,删除最大值左右两边的值,将最大值位置的值修改为左右两边的和减去中间的数重新进入优先队列,循环处理直到拿到要求的数据个数。这块太巧妙了,直接把算法的时间复杂度搞到了O(n)。
    代码:
    #include <iostream>  
    #include <algorithm>  
    #include <cmath>  
    #include <vector>  
    #include <string>  
    #include <cstring>
    #include <queue>
    #pragma warning(disable:4996)  
    using namespace std;
    
    #define impossible -1*(1e9+7)
    
    struct no
    {
    	long long val;
    	int pos;
    
    	friend bool operator<(no n1, no n2)
    	{
    		return n1.val < n2.val;
    	}
    }node[100005];
    
    int n;
    long long val[100005];
    
    int main()
    {
    	//freopen("i.txt", "r", stdin);
    	//freopen("o.txt", "w", stdout);
    
    	int i, k, temp, out;
    	unsigned long long sum;
    	no n_temp;
    	priority_queue<no>q;
    
    	scanf("%d", &n);
    
    	for (i = 0; i < n; i++)
    	{
    		scanf("%d", &temp);
    		node[i].val = temp;
    		node[i].pos = i;
    		q.push(node[i]);
    
    		val[i] = temp;
    	}
    
    	k = 0;
    	sum = 0;
    	out = n / 3;
    	while (true)
    	{
    		n_temp = q.top();
    		if (n_temp.val <= 0)
    			break;
    		if (val[n_temp.pos] != impossible)
    		{
    			q.pop();
    			sum += n_temp.val;
    
    			int le = n_temp.pos;
    			int ri = n_temp.pos;
    			while (val[(le - 1 + n) % n] == impossible)
    			{
    				le--;
    			}
    			while (val[(ri + 1 + n) % n] == impossible)
    			{
    				ri++;
    			}
    
    			no nn_temp;
    			nn_temp.val = val[(le - 1 + n) % n] + val[(ri + 1 + n) % n ] - n_temp.val;
    			nn_temp.pos = n_temp.pos;
    
    			q.push(nn_temp);
    
    			val[n_temp.pos] = nn_temp.val;
    			val[(le - 1 +  n) % n] = impossible;
    			val[(ri + 1 +  n) % n] = impossible;
    
    			k++;
    		}
    		else
    		{
    			q.pop();
    		}
    		if (k == out)
    			break;
    	}
    
    	printf("%lld
    ", sum);
    	//system("pause");
    	return 0;
    }


    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    按指定上下限区间进行数据统计的示例.sql
    树形数据层次显示处理示例.sql
    Flexi传授如何说服自己的老板采用Node.js
    sed的用法[转]
    [bash] string operators
    [shell script]脚本实现目录和文件名显示
    Bash快捷键
    [bash] Condition Tests
    整理一下博客
    老爸的工具箱之:根据日期批量重命名照片
  • 原文地址:https://www.cnblogs.com/lightspeedsmallson/p/4928120.html
Copyright © 2020-2023  润新知