基准时间限制:1 秒 空间限制:131072 KB 分值: 40
难度:4级算法题
X轴上有N个点,每个点除了包括一个位置数据X[i],还包括一个权值W[i]。点P到点P[i]的带权距离 = 实际距离 * P[i]的权值。求X轴上一点使它到这N个点的带权距离之和最小,输出这个最小的带权距离之和。
Input
第1行:点的数量N。(2 <= N <= 10000) 第2 - N + 1行:每行2个数,中间用空格分隔,分别是点的位置及权值。(-10^5 <= X[i] <= 10^5,1 <= W[i] <= 10^5)
Output
输出最小的带权距离之和。
Input示例
5 -1 1 -3 1 0 1 7 1 9 1
Output示例
20
思路:列方程 || 拆分
一,列方程:将所有点按下标排序,对于最小值ans所取的点一定在所有相邻的两点之间,可从右到左遍历所有相邻两点,对于点 d[i]的下标d[i].x和权值d[i].w,开始 ans所取的点在最右点,对于 方程ans=ax+b, a=所有点的权值和, b=所有点的权值*下标和, 那么遍历到 d[i]和d[i+1]点之间最左端 l=d[i].x ,最右端 r=d[i+1].x ,则 a-=2*d[i+1].w; b-=2*d[i+1].w*d[i+1].x; 那么对于 ans=ax+b, 首先由题目可以肯定 ans>0,那么只要对 a分情况讨论即可
a>0, 则 x=l 时 ans最小 为 a*l+b;
a<0, 则 x=r 时 ans最小 为 a*r+b;
二,拆分: 对于下标为x,权值为w的点可以将其 拆分成 w个 下标为x,权值为1的点,这样就可利用中位数来求得最小值
Code 1 :
//列方程组
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
struct node{
int x;
LL w;
bool operator<(const node &p)const{
return x<p.x;
}
};
const int MAX_N=10005;
int n;
node d[MAX_N];
int main()
{
ios::sync_with_stdio(false);
cin>>n;
LL a=0,b=0;
for(int i=0;i<n;++i)
{
cin>>d[i].x>>d[i].w;
a+=d[i].w; b-=d[i].w*d[i].x;
}
sort(d,d+n);
LL ans=1e15;
int l,r;
for(int i=n-2;i>=0;--i)
{
l=d[i].x; r=d[i+1].x;
a-=2*d[i+1].w; b+=2*d[i+1].w*d[i+1].x;
if(a>=0){
ans=min(ans,a*l+b);
}else ans=min(ans,a*r+b);
}
cout<<ans<<endl;
return 0;
}
Code 2 :
//拆分
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
struct node{
int x;
LL w;
bool operator<(const node &p)const{
return x<p.x;
}
};
const int MAX_N=10005;
int n;
node d[MAX_N];
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=0;i<n;++i)
cin>>d[i].x>>d[i].w;
sort(d,d+n);
LL ans=0,t;
int l=0,r=n-1;
while(l<r){
t=min(d[l].w,d[r].w);
ans+=t*(d[r].x-d[l].x);
d[l].w-=t; d[r].w-=t;
if(d[l].w==0) l++;
if(d[r].w==0) r--;
}
cout<<ans<<endl;
return 0;
}