POJ3253-Fence Repair

15 篇文章 0 订阅
8 篇文章 0 订阅

将题目想成一棵二叉树,一块木板为父节点,切割后的两段长度为它的子节点。

如此切割的开销为父节点木板对应的长度。

总开销为所有含有子节点的父节点对应长度只和。

等于这棵树的各叶节点乘以节点的深度。

因此最短的板应是深度最大的叶子节点之一。

最短的板与次短的板的节点是兄弟节点。

将l按大小顺序排序,最短的板l1和次短的板l2是由长度为(l1+l2)的板得到的。

递归地将这n-1块木板的问题求解即可。

#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 20000;
int plk[MAXN+2];

int main()
{
    int n;
    scanf("%d", &n);
    
    int i;
    for (i = 0; i < n; i++) {
        scanf("%d", &plk[i]);
    }
    
    sort(plk, plk+n);
    
    long long ret = 0;
    i = 1;
    while (i < n) {
        plk[i] = plk[i]+plk[i-1];
        ret += plk[i];
        sort(plk+i, plk+n);
        i++;
    }
    
    printf("%lld\n", ret);
    
    return 0;
}

超时了尼玛!!

问题在于我只要找到木板中最短的和次短的,没有必要对整个数组进行排序。

那么问题来了。

如何用一个for循环找到最短和次短呢?

#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 20000;
int plk[MAXN+2];

int main()
{
    int n;
    scanf("%d", &n);
    
    int i;
    for (i = 0; i < n; i++) {
        scanf("%d", &plk[i]);
    }
    
    long long ans = 0;
    
    while (n > 1) {
        int a = 0;
        int b = 1;
        if (plk[a] > plk[b]) {
            swap(a, b);
        }
        
        for (i = 2; i < n; i++) {
            if (plk[i] < plk[a]) {
                b = a;
                a = i;
            }
            else if (plk[i] < plk[b])
                b = i;
        }
        
        int t = plk[a] + plk[b];
        ans += t;
        
        if (a == n -1) {
            swap(a, b);
        }
        plk[a] = t;
        plk[b] = plk[n-1];
        n--;
    }
    
    printf("%lld\n", ans);
    
    return 0;
}

a,b分别代表了最短和次短的位置,通过遍历数组找到plk[a]和plk[b]。

而且不断缩小遍历范围。



但是依旧有更好的算法。利用优先队列依旧取出最小的和次小的。

#include <cstdio>
#include <queue>
#include <functional>
#include <algorithm>

using namespace std;

priority_queue<int, vector<int>, greater<int> > que;

int main()
{
    int n;
    scanf("%d", &n);
    
    int i;
    int temp;
    for (i = 0; i < n; i++) {
        scanf("%d", &temp);
        que.push(temp);
    }
    
    long long ans = 0;
    while (que.size() > 1) {
        int a, b;
        a = que.top();
        que.pop();
        b =  que.top();
        que.pop();
        
        ans += a + b;
        que.push(a + b);
    }
 
    printf("%lld\n", ans);
    
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值