• 【u009】瑞瑞的木板


    Time Limit: 1 second
    Memory Limit: 128 MB

    【问题描述】

      瑞瑞想要亲自修复在他的一个小牧场周围的围栏。他测量栅栏并发现他需要N(1≤N≤20,000)根木板,每根的长度为整数Li(1≤Li≤50,000)。于是,他神奇地买了一根足够长的木板,长度为所需的N根木板的长度的总和,他决定将这根木板切成所需的N根木板。(瑞瑞在切割木板时不会产生木屑,不需考虑切割时损耗的长度) 瑞瑞切割木板时使用的是一种特殊的方式,这种方式在将一根长度为x的模板切为两根时,需要消耗x个单位的能量。瑞瑞拥有无尽的能量,但现在提倡节约能量,所以作为榜样,他决定尽可能节约能量。显然,总共需要切割N-1次,问题是,每次应该怎么切呢?请编程计算最少需要消耗的能量总和。


    【输入格式】

    第一行: 整数N,表示所需木板的数量 第2到N+1行: 每行为一个整数,表示一块木板的长度

    【输出格式】

    一个整数,表示最少需要消耗的能量总和

    【数据规模】

    Sample Input1

    3
    8
    5
    8
    
    
    
    
    
    
    

    Sample Output1

    34
    
    
    

    【样例说明】

    将长度为21的木板,第一次切割为长度为8和长度为13的,消耗21个单位的能量,第二次将长度为13的木板切割为长度为5和8的,消耗13个单位的能量,共消耗34个单位的能量,是消耗能量最小的方案。
    【题解】

    这题需要反过来想。

    把一块长木板分成n个小木板,它的逆过程。就是

    把n块小木板拼成1块长木板。

    然后本来是每次消耗所需要切割的长度的体力。

    现在变成消耗掉两块木板拼起来的长度等价的体力。

    然后依然是操作n-1次。

    则。每次只要选择最短和次短的两块木板拼起来就可以了。这样可以保证每次进行的操作都是花费最小的。

    而维护最小值。需要用到堆(小根堆)。

    【代码】

    #include <cstdio>
    
    __int64 dui[20001] = { 0 };//记录堆的信息。小根堆。
    int n,pos;
    
    void up_adjust(int p)//从位置p开始往上调整堆。
    {
    	__int64 x = dui[p];//先记录堆中这个元素的大小。
    	int i = p, j = p / 2;
    	while (j > 0)//如果还没到根节点。
    	{
    		if (x < dui[j])//如果需要调整则调整
    		{
    			dui[i] = dui[j];
    			i = j;
    			j = i / 2;
    		}
    		else//不需要调整了就结束。
    			break;
    	}
    	dui[i] = x;
    	pos = i;//记录下新的位置。方便继续尝试往下调整。
    }
    
    void down_adjust(int p)//从位置p开始往下调整
    {
    	__int64 x = dui[p];
    	int i = p, j = p * 2;
    	while (j <= dui[0])//如果没到叶子节点
    	{
    		if (j < dui[0] && dui[j + 1] < dui[j])//如果右儿子更小则和右儿子尝试交换。
    			j++;
    		if (x > dui[j])//如果需要调整,则调整。
    		{
    			dui[i] = dui[j];
    			i = j;
    			j = i * 2;
    		}
    		else
    			break;
    	}
    	dui[i] = x;//把这个元素放到新的位置。
    }
    
    void input_data()
    {
    	scanf("%d", &n);//输入数据
    	for (int i = 1; i <= n; i++)//依次把n个数据加入到堆中去。
    	{
    		int x;
    		scanf("%d", &x);
    		dui[0]++;
    		dui[dui[0]] = x;
    		up_adjust(dui[0]);//因为加到叶子节点。所以要先往上调整。
    		down_adjust(pos);//然后再往下调整。
    	}
    }
    
    void get_ans()
    {
    	__int64 ans = 0;//答案一开始为0
    	for (int i = 1; i <= n - 1; i++)
    	{
    		__int64 x = dui[1];//取出一个元素。进行调整
    		dui[1] = dui[dui[0]];
    		dui[0] --;
    		down_adjust(1);
    		__int64 y = dui[1];//再取出另一个次小的。再调整。
    		dui[1] = dui[dui[0]];
    		dui[0]--;
    		down_adjust(1);
    		__int64 z = x + y;//把这两个木块拼在一起。
    		ans += z;//累加答案。
    		dui[0]++;//把拼出来的新木板再加入到堆中去。
    		dui[dui[0]] = z;
    		up_adjust(dui[0]);//向上调整。再向下调整即可。
    		down_adjust(pos);
    	}
    	printf("%I64d
    ", ans);
    }
    
    int main()
    {
    	input_data();
    	get_ans();
    	return 0;
    }


  • 相关阅读:
    寒假作业3:简化电梯优化
    线段树2
    线段树1
    数字游戏(二)
    P1352 没有上司的舞会
    加分二叉树
    数字转换
    BLO-Blockade
    树上倍增法求LCA
    种西瓜
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632298.html
Copyright © 2020-2023  润新知