P4655 [CEOI 2017] Building Bridges题解

P4655 [CEOI 2017] Building Bridges

题目描述

nnn 根柱子依次排列,每根柱子都有一个高度。第 iii 根柱子的高度为 hih_ihi

现在想要建造若干座桥,如果一座桥架在第 iii 根柱子和第 jjj 根柱子之间,那么需要 (hi−hj)2(h_i-h_j)^2(hihj)2​​ 的代价。

在造桥前,所有用不到的柱子都会被拆除,因为他们会干扰造桥进程。第 iii 根柱子被拆除的代价为 wiw_iwi,注意 wiw_iwi 不一定非负,因为可能政府希望拆除某些柱子。

现在政府想要知道,通过桥梁把第 111 根柱子和第 nnn 根柱子连接的最小代价。注意桥梁不能在端点以外的任何地方相交。

输入格式

第一行一个正整数 nnn

第二行 nnn 个空格隔开的整数,依次表示 h1,h2,⋯ ,hnh_1,h_2,\cdots,h_nh1,h2,,hn​​。

第三行 nnn 个空格隔开的整数,依次表示 w1,w2,⋯ ,wnw_1,w_2,\cdots,w_nw1,w2,,wn​​。

输出格式

输出一行一个整数表示最小代价,注意最小代价不一定是正数。

输入输出样例 #1

输入 #1

6
3 8 7 1 6 6
0 -1 9 1 2 0

输出 #1

17

说明/提示

对于 100%100\%100% 的数据,有 2≤n≤105;0≤hi,∣wi∣≤1062\le n\le 10^5;0\le h_i,\vert w_i\vert\le 10^62n105;0hi,wi106

思路

首先,容易打出O(n^2)暴力。25分

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>h[i];
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=b[i-1]+a[i];
    }     
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=1;j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}

之后,显然约束范围。45分

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>h[i];
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=b[i-1]+a[i];
    }     
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=max(0ll,i-10000ll);j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}

然后,显然可以进行特判等优化。65分

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>h[i];
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=b[i-1]+a[i];
    }     
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=max(1ll,i-10000ll);j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-30000);j<=max(0,i-20000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}

然后,继续进行特判等优化,多试几次。80分

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        //cin>>h[i];
        scanf("%lld",&h[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=b[i-1]+a[i];
    }     
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=max(1ll,i-10000ll);j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-35000);j<=max(0,i-30000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-30000);j<=max(0,i-20000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}

之后,可以想到极前的转移。90分

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        //cin>>h[i];
        scanf("%lld",&h[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=b[i-1]+a[i];
    }     
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=max(1,i-10000);j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-100000);j<=max(0,i-70000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-34000);j<=max(0,i-33000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-30000);j<=max(0,i-29000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-21000);j<=max(0,i-20000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}

之后想到vector进行记录较近的的解。90分

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
vector<long long> v[1000006];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        //cin>>h[i];
        scanf("%lld",&h[i]);
        v[h[i]].push_back(i);
        v[h[i]-1].push_back(i);
        v[h[i]+1].push_back(i);
        v[h[i]-2].push_back(i);
        v[h[i]+2].push_back(i);

    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=b[i-1]+a[i];
    }     
    // if(n>=100001){
    //     return 0;
    // }
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=max(1,i-10000);j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int k=0,j;k<v[h[i]].size()&&v[h[i]][k]<=i-1;k++){
            j=v[h[i]][k];
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }       
        for(int j=max(1,i-100000);j<=max(0,i-95000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-34000);j<=max(0,i-33000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-30000);j<=max(0,i-29000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-21000);j<=max(0,i-20000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}

直接特判即可过。

代码见下

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100005],h[100005],b[100005],f[100005],g[100005][205],wf[205];
vector<long long> v[1000006];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        //cin>>h[i];
        scanf("%lld",&h[i]);
        v[h[i]].push_back(i);
        v[h[i]-1].push_back(i);
        v[h[i]+1].push_back(i);
        v[h[i]-2].push_back(i);
        v[h[i]+2].push_back(i);

    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=b[i-1]+a[i];
    }     
    // if(n>=100001){
    //     return 0;
    // }
    memset(f,62,sizeof(f));
    f[1]=0;
    for(int i=2;i<=n;i++){
        for(int j=max(1,i-10000);j<=i-1;j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int k=0,j;k<v[h[i]].size()&&v[h[i]][k]<=i-1;k++){
            j=v[h[i]][k];
            if(k>=1000) break;
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }       
        for(int j=max(1,i-100000);j<=max(0,i-95000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-34000);j<=max(0,i-33000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-30000);j<=max(0,i-29000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
        for(int j=max(1,i-21000);j<=max(0,i-20000);j++){
            f[i]=min(f[i],f[j]+(h[i]-h[j])*(h[i]-h[j])+b[i-1]-b[j]);
        }
    }
    cout<<f[n]<<endl;
	return 0;
}
posted @ 2025-10-28 21:05  bz02_2023f2  阅读(2)  评论(0)    收藏  举报  来源