现在的位置: 首页 > 综合 > 正文

【贪心+二分】 居民迁移

2018年04月13日 ⁄ 综合 ⁄ 共 3142字 ⁄ 字号 评论关闭

    把所有城市居民看成是seg(l,r,n),(其中l为该城市居民能迁徙到的最左点,r为能迁徙到的最右点),统一调度。

  贪心调度策略:

  • 从左到右依次枚举城市(1到n):挑选seg来填充它们。填充尽量多。(有一个上限阈值为maxN)
  • 挑选seg的时候,事先对seg按照<l,r>排序,挑选seg.r尽量小的,并且seg.l小于当前城市坐标的。因为r越大,越可以留到后面。r为某个城市居民最右可以迁徙到的点。
  • 另外挑选seg的时候,如果seg.r比当前城市坐标还要小,说明当前情况下,该seg居民本来只能安顿到之前的城市里,但之前的城市都满员了,所以调度失败。
  防止溢出:
  • 程序中要注意10^9的两个数+或-是不会溢出的。
  • 唯一一处需要longlong的是ss,它统计所有城市居民人口总和(100000*10^9),再求平均。
  双线性扫描:
  • 另外把seg放在vector<seg>&q里,从左到右对城市p【】和居民seg【】进行双线性扫描
  • 扫描过程中注意使用li,ln保存上一次seg【】点的位置和值。
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
typedef long long int ll;
struct seg{
    int l,r,n;
    seg(int x,int y,int z):l(x),r(y),n(z){}
};
bool test(const vector<int> &p,const vector<seg> &q,int maxN){//q is sorted
    list<seg> segs;
    int N=p.size(),M=q.size();
    if(q.size()==0) return true; 
    int li=0,ln=q[0].n;
    for(int i=0;i<N;i++){
        int x=p[i];
        int n=maxN;
        if(q[li].r<x) return false;
        for(int j=li;j<=M;j++){
          if(j==M) return true;
          if(q[j].l>x) {
            li=j,ln=q[j].n;
            break;
          }
          int tmp=j==li?ln:q[j].n;
          n-=tmp;
          if(n<0) {
            li=j,ln=0-n;
            break;
          }
          // if n>=0 j++....
        }
    }
    return false;
}
typedef pair<int,int> Pair;
int getMin(vector<Pair>&points,int R){
    int N=points.size();
    if(N==0) return 0;
    
    vector<int>p;vector<seg>q;int tt=0;ll ss=0;//@patch: ss can overflow
    for(int i=0;i<N;i++){
        Pair &t=points[i];
        p.push_back(t.first);
        if(t.second)//@patch: ignore seg with seg.n=0
          q.push_back(seg(t.first-R,t.first+R,t.second));
        tt=max(tt,t.second);//@patch
        ss+=t.second;
    }
    int r=tt,l=(ss+N-1)/N; //@patch:ss is long long
    while(l!=r){
        int mid=(l+r)/2;// l+r can overflow? no. l<=10^9,r<=10^9. 2*10^9<2^31
        if(test(p,q,mid)) {r=mid;}
        else l=mid+1;
    }
    return l;
}
int main(){
    int N;cin>>N;
    for(int i=0;i<N;i++){
        int n,r;cin>>n>>r;
        vector<Pair> ps(n,Pair(-1,-1));
        for(int j=0;j<n;j++){ int x,y;cin>>x>>y;ps[j]=Pair(x,y); }
        sort(ps.begin(),ps.end());    
        cout<<getMin(ps,r)<<endl;
    }
    return 0;
}

顺带贴一下用链表存储seg【】的方法:

链表实现seg【】,优点是每次只需要处理头结点对应seg。每次用完一个seg就可以删掉该头节点。

缺点是因为用到二分法,需要多次尝试(test(),具体为贪心法),每次尝试需要新建一个链表seg【】。

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;

struct seg{
    int l,r,n;
    seg(int x,int y,int z):l(x),r(y),n(z){}
};
bool test(vector<int> &p,vector<seg> &q,int maxN){//q is sorted
    list<seg> segs;
    int N=p.size();
    for(int i=0;i<q.size();i++) segs.insert(segs.end(),q[i]);/////////////

    for(int i=0;i<N;i++){
        int x=p[i];
        int n=maxN;
        for(list<seg>::iterator it=segs.begin();it!=segs.end();){
            seg &s=*it;//@error: seg s is a copy, so should be seg &s
            if(s.r<x) return false;//some people can go no where
            if(s.l<=x){//[s.l,s.r] has x. and people can go here
               n-=s.n;
               if(n<0) s.n=0-n;
               else s.n=0;
               if(s.n==0) it=segs.erase(it);
               if(n<=0) break;
            }
            else break;
        }
    }
    return segs.size()==0;
}
typedef pair<int,int> Pair;
int getMin(vector<Pair>&points,int R){
    int N=points.size();
    if(N==0) return 0;
    
    vector<int>p;vector<seg>q;int ss=0;long long int tt=0;/////////////////////
    for(int i=0;i<N;i++){
        Pair &t=points[i];
        p.push_back(t.first);
        if(t.second)///////////////////////
        q.push_back(seg(t.first-R,t.first+R,t.second));
        ss=max(ss,t.second);//////////////////////
        tt+=t.second;
    }
    int r=ss,l=tt/N+(tt%N?1:0);///////////////////////
    while(l!=r){
        int mid=(l+r)/2;
        if(test(p,q,mid)) {r=mid;}
        else l=mid+1;
    }
    return l;
}
int main(){
    int N;cin>>N;
    for(int i=0;i<N;i++){
        int n,r;cin>>n>>r;
        vector<Pair> ps(n,Pair(-1,-1));
        for(int j=0;j<n;j++){ int x,y;cin>>x>>y;ps[j]=Pair(x,y); }
        sort(ps.begin(),ps.end());    
        cout<<getMin(ps,r)<<endl;
    }
    return 0;
}

抱歉!评论已关闭.