题目
数轴上有n(n<=1e3)个位置,表示n个房子所在位置h1,...,hn
还有两个坐标点p1,p2,根据这两个点,构造出两个数组d1和d2
其中d[1][i]=abs(hi-p1),即n个点到p1的距离数组,d[2][i]同理,d[2][i]=abs(hi-p2)
现给出d1、d2并将两个数组内部打乱,问是否存在一组h1,...,hn、p1、p2满足条件
若存在,输出Yes,并且需要将hi,p1,p2控制在[0,2e9]内,存在多组的话输出任意一组即可
若不存在,输出No
实际是t(t<=1e3)组样例,但保证sumn不超过2e3
思路来源
xjb搞通过,优化了一下代码长度
题解
先将d1、d2排序后,不妨先钦定p1在p2左侧(或重合),
如果在右侧的话,可以镜像翻转整个数轴上的所有点使之在左侧
如果存在一组解的话,一定有某个点距离p1为d[1][n],考虑枚举p2到这个点的距离d[2][i],
则p1、p2之间的距离只可能是d[1][n]+d[2][i]或abs(d[1][n]-d[2][i]),
其中,
1.d[1][n]+d[2][i]对应了这个点在p1、p2中间,
2. d[1][n]-d[2][i]对应了在p2右侧
3. d[2][i]-d[1][n]对应了在p1左侧
后两种情况可以用绝对值统一
考虑枚举了这个距离dis之后,设当前dis=100,类似地,将剩余hi点分为三类
1. p2hi-p1hi=100
2. p1hi+p2hi=100
3. p1hi-p2hi=100
考虑从大到小枚举d1内的距离x,先判断d2中是否存在x+100,若不存在的话,
因为距离都是非负的,根据x和100的大小关系决定d2内用x-100还是100-x,
就确定了三者的优先级,先判断x+100是基于d1大的优先和d2大的匹配,
若d1大的能和d2大的匹配却没有匹配的话,d1小的去和d2大的匹配是不优的
另一种贪心方法是,同时维护d1和d2当前没被匹配的最大元素x(第一种、第三种情况),
去另一堆里找x-100是否存在,先把两堆中大于100的元素匹配完,
剩下的两堆中的元素,就只能满足第二种情况了,对应匹配即可,
不过写起来比上面的略麻烦一些
有解后,需要将答案偏移到[0,2e9]之间,这里是令所有点都减去最小点的坐标,
相当于令最小点平移到了0,其他点同时右移到对应位置了
长度2e9一定有解的证明
1. 考虑p1和p2的距离大于1e9时,p1左侧和p2右侧都不能有点,否则违背了d1,d2<=1e9的限制
而其中最极限的情况是,p1在0,p2在2e9,1e9处有hi,满足最大距离=2e9
2. 而p1和p2的距离小于1e9时,左右范围[p2-1e9,p1+1e9],在p2>=p1的情况下,上述区间恒小于等于2e9
代码1
倒序贪心
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,off=-1e9;
int t,n,v,pos[N],a[N],b[N],p1,p2,dis;
bool check(int x){
int c=0;
map<int,int>mp;
for(int i=0;i<n;i++)mp[b[i]]++;
for(int i=n-1;i>=0;--i){
if(x<=1e9-a[i]&&mp[a[i]+x]){
mp[a[i]+x]--;
pos[c++]=p1-a[i];
}
else if(a[i]>x&&mp[a[i]-x]){
mp[a[i]-x]--;
pos[c++]=p1+a[i];
}
else if(mp[x-a[i]]){
mp[x-a[i]]--;
pos[c++]=p1+a[i];
}
else return 0;
}
cout<<"YES"<<endl;
int mn=min(p1,p2);
for(int i=0;i<n;++i)mn=min(mn,pos[i]);
for(int i=0;i<n;++i){
cout<<pos[i]-mn<<(i==n-1?'\n':' ');
}
cout<<p1-mn<<" "<<p2-mn<<endl;
return 1;
}
void solve(){
cin>>n;
for(int j=0;j<n;++j)cin>>a[j];
for(int j=0;j<n;++j)cin>>b[j];
sort(a,a+n);sort(b,b+n);
for(int i=0;i<n;++i){
dis=abs(b[i]-a[n-1]);
p1=off,p2=off+dis;
if(check(dis))return;
dis=abs(b[i]+a[n-1]);
p1=off,p2=off+dis;
if(check(dis))return;
}
cout<<"NO"<<endl;
}
int main(){
cin>>t;
while(t--){
solve();
}
return 0;
}
代码2
维护两堆内当前最大元素
#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e3+10,off=-1e9;
int t,n,v,pos[N],p1,p2;
vector<int>d[2];
void pt(){
cout<<"YES"<<endl;
int mn=min(p1,p2);
for(int i=0;i<n;++i){
mn=min(mn,pos[i]);
}
for(int i=0;i<n;++i){
cout<<pos[i]-mn<<(i==n-1?'\n':' ');
}
cout<<p1-mn<<" "<<p2-mn<<endl;
}
bool check(int dis){
map<int,int>mp1,mp2;
for(auto &v:d[0])mp1[v]++;
for(auto &v:d[1])mp2[v]++;
auto x=mp1.end();x--;
auto y=mp2.end();y--;
int c=0;
while(c<n){
while(x!=mp1.begin() && !mp1[x->first])x--;
while(y!=mp2.begin() && !mp2[y->first])y--;
int v1=x->first,v2=y->first;
if(v1>=v2){
if(mp2[abs(v1-dis)]<mp1[v1])return 0;
mp2[abs(v1-dis)]-=mp1[v1];
for(int j=0;j<mp1[v1];++j){
pos[c++]=p1+v1;
}
mp1[v1]=0;
}
else{
if(mp1[abs(v2-dis)]<mp2[v2])return 0;
mp1[abs(v2-dis)]-=mp2[v2];
for(int j=0;j<mp2[v2];++j){
pos[c++]=p2-v2;
}
mp2[v2]=0;
}
}
return 1;
}
void solve(){
for(int i=0;i<n;++i){
int dis=abs(d[1][i]-d[0][n-1]);
p1=off,p2=off+dis;
if(check(dis)){
pt();
return;
}
dis=abs(d[1][i]+d[0][n-1]);
p1=off,p2=off+dis;
if(check(dis)){
pt();
return;
}
}
cout<<"NO"<<endl;
}
int main(){
cin>>t;
while(t--){
cin>>n;
for(int i=0;i<2;++i){
d[i].clear();
for(int j=0;j<n;++j){
cin>>v;
d[i].push_back(v);
}
sort(d[i].begin(),d[i].end());
}
solve();
}
return 0;
}