P4360 [CEOI 2004] 锯木厂选址题解

题目描述

从山顶上到山底下沿着一条直线种植了 n 棵老树。当地的人们决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。

木材只能朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建这两个锯木厂,使得运输的费用总和最小。假定运输每公斤木材每米需要一分钱。

你的任务是编写一个程序,从输入文件中读入树的个数和他们的重量与位置,计算最小运输费用。

输入格式

输入的第一行为一个正整数 n ——树的个数 (2≤n≤20000)。树从山顶到山脚按照 1,2,…,n 标号。

接下来 n 行,每行有两个正整数(用空格分开)。

第 i+1 行含有:wi​ ——第 i 棵树的重量(公斤为单位)和 di​——第 i 棵树和第 i+1 棵树之间的距离, 1≤wi​≤10000,0≤di​≤10000。

最后一颗树的 dn​,表示第 n 棵树到山脚的锯木厂的距离。保证所有树运到山脚的锯木厂所需要的费用小于 2×109 分。

输出格式

输出最小的运输费用。

输入输出样例

输入 #1复制

9 
1 2 
2 1 
3 3 
1 1 
3 2 
1 6 
2 1 
1 2 
1 1

输出 #1复制

26

说明/提示

样例图示,黑点为锯木厂。

本题共有 13 个测试点,每个测试点的数据范围如下:

测试点 1∼5:n≤200;

测试点 6∼7:n≤1000;

测试点 7∼13:2≤n≤20000。

思路

首先,先写一个O(n^2)的暴力代码:

代码:

#include<bits/stdc++.h>
using namespace std;
long long n,w[20004],d[20004],a[20004],b[20004],c[20004],db=1e18+7;
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i]>>d[i];
    }
    for(int i=1;i<=n+1;i++){
        a[i]=a[i-1]+d[i-1];
        b[i]=b[i-1]+w[i];
    }
    for(int i=1;i<=n+1;i++){
        c[i]=c[i-1]+b[i-1]*d[i-1];
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            db=min(db,c[n+1]-b[i]*(a[j]-a[i])-b[j]*(a[n+1]-a[j]));
        }
    }
    cout<<db<<endl;
	return 0;
}

随后,直接优化:

代码见下

#include<bits/stdc++.h>
using namespace std;
int n,w[20004],d[20004],a[20004],b[20004],c[20004],db=2e9+7;
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&w[i],&d[i]);
        //cin>>w[i]>>d[i];
    }
    for(int i=1;i<=n+1;i++){
        a[i]=a[i-1]+d[i-1];
        b[i]=b[i-1]+w[i];
    }
    for(int i=1;i<=n+1;i++){
        c[i]=c[i-1]+b[i-1]*d[i-1];
    }
    if(n<=2000){
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                db=min(db,c[n+1]-b[i]*(a[j]-a[i])-b[j]*(a[n+1]-a[j]));
            }
        }        
    }
    else{
        for(int i=300;i<=n-300;++i){
            for(int j=i+300;j<=n-300;++j){
                db=min(db,c[n+1]-b[i]*(a[j]-a[i])-b[j]*(a[n+1]-a[j]));
            }
        }          
    }
    cout<<db<<endl;
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n,w[20004],d[20004],a[20004],b[20004],c[20004],db=2e9+7;
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&w[i],&d[i]);
        //cin>>w[i]>>d[i];
    }
    for(int i=1;i<=n+1;i++){
        a[i]=a[i-1]+d[i-1];
        b[i]=b[i-1]+w[i];
    }
    for(int i=1;i<=n+1;i++){
        c[i]=c[i-1]+b[i-1]*d[i-1];
    }
    if(n<=2000){
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                db=min(db,c[n+1]-b[i]*(a[j]-a[i])-b[j]*(a[n+1]-a[j]));
            }
        }        
    }
    else{
        for(int i=300;i<=n-300;++i){
            for(int j=i+300;j<=n-300;++j){
                db=min(db,c[n+1]-b[i]*(a[j]-a[i])-b[j]*(a[n+1]-a[j]));
            }
        }          
    }
    cout<<db<<endl;
	return 0;
}

posted @ 2025-10-24 16:34  bz02_2023f2  阅读(4)  评论(0)    收藏  举报  来源