Codeforces Round #824 (Div. 2) E. House Planning(枚举+贪心)

题目

数轴上有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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值