zoukankan      html  css  js  c++  java
  • bzoj1835基站选址(dp+线段树)

    Description
    有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。

    Input
    输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN

    Output
    一行一个数,表示最小花费

    Sample Input
    3 2 1 2 2 3 2 1 1 0 10 20 30

    Sample Output
    4

    分析:
    设f[i]为在第i个村庄建通讯基站时的最小花费

    则f[i]=min{f[j]+cost[j][i]}+c[i],1<=j<=i-1

    cost[j][i]表示最后两个基站分别建在j和i时对于中间村庄的补偿W之和
    暴力求cost[j][i]的话,时间复杂度会达到O(n^3),对于本题的数据规模来说是不令人满意的

    假如f[j]与cost[j][i]为定值的话,显然可以用数据结构来维护min(f[j]+cost[j][i])
    维护区间信息,我们一下子就想到了线段树

    然而当i变为i+1时,cost[j][i]的值会变更,因为此时的基站的位置改变,需要给出的补偿也会改变
    那么这个变化显然是由原来被i覆盖的村庄所影响的,

    设l[x],r[x]表示在[l[x],r[x]]范围内建造基站村庄x能被覆盖

    所以我们可以计算出每个村庄可以被覆盖的左端点与右端点l与r,

    每次处理完x位置,枚举所有r[a]=x的村庄a,然后在线段树中把[1,l[a]-1]都加上w[a]
    (在l[a]之前建一座基站覆盖不到a,花费要加上w,第二座基站在r[a]覆盖了a)

    此外,由于答案最优时不一定需要在最后一个点建基站,我们可以新增一个点,
    w[++n]=+oo,c[n]=0,++k
    这样子就可以保证答案一定是f[n]

    最外层循环基站个数从1到k时注意每次保留最优解,因为不一定建k个基站是最优的

    最后总结一下写这个代码时犯的错误:

    if (d[r[i]]>d[i]+s[i]) r[i]–;
    //r[i]和l[i]记录的都是村庄的编号,而比较的时候比较的是距离

    不会用lower_bound,vector

    输出的变量写错(超zz)

    ask,add时没有特判l>r的情况
    //在处理第一个和最后一个时很有可能出现这种情况

    tree[bh].lazy=0;
    //最玄学的错误,因为是重构线段树,所以经过的每一个线段树的节点的标记都要清零

    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    
    using namespace std;
    
    const int INF=0x33333333;
    const int N=100010;
    int n,k;
    int d[N],c[N],s[N],w[N],l[N],r[N],f[N];
    struct node{
        int x,y,mn,lazy;
    };
    node tree[N<<2];
    vector<int> g[N];
    int ans;
    
    inline void update(int bh)
    {
        tree[bh].mn=min(tree[bh<<1].mn,tree[bh<<1|1].mn);
    }
    
    inline void push(int bh)
    {
        if (tree[bh].lazy&&tree[bh].x!=tree[bh].y)
        {
            int lc=bh<<1;
            int rc=bh<<1|1;
            tree[lc].lazy+=tree[bh].lazy;
            tree[rc].lazy+=tree[bh].lazy;
            tree[lc].mn+=tree[bh].lazy;  //最小花费 
            tree[rc].mn+=tree[bh].lazy;
            tree[bh].lazy=0;
        }
        return;
    }
    
    void build(int bh,int l,int r)
    {
        tree[bh].x=l;
        tree[bh].y=r;
        tree[bh].lazy=0;  //
        if (l==r)
        {
            tree[bh].mn=f[l];
            return;
        }
        int mid=(l+r)>>1;
        build(bh<<1,l,mid);
        build(bh<<1|1,mid+1,r);
        update(bh);
    }
    
    int ask(int bh,int l,int r)
    {
        if (l>r) return 0;  //特判
        push(bh);
        if (tree[bh].x>=l&&tree[bh].y<=r)
        {
            return tree[bh].mn;
        }
        int ans=1000010;
        int mid=(tree[bh].x+tree[bh].y)>>1;
        if (l<=mid) ans=min(ans,ask(bh<<1,l,r));
        if (r>mid) ans=min(ans,ask(bh<<1|1,l,r));
        return ans;
    }
    
    void add(int bh,int l,int r,int z)
    {
        if (l>r) return;  // 
        push(bh);
        if (tree[bh].x>=l&&tree[bh].y<=r)
        {
            tree[bh].mn+=z;
            tree[bh].lazy+=z;
            return;
        }
        int mid=(tree[bh].x+tree[bh].y)>>1;
        if (l<=mid) add(bh<<1,l,r,z);
        if (r>mid) add(bh<<1|1,l,r,z);
        update(bh);    ///
    }
    
    void doit()
    {
        int i,j;
        for (i=1;i<=k;i++)   //外层循环基站个数
        {
            if (i==1)
            {
                int t=0;
                for (j=1;j<=n;j++)
                {
                    f[j]=t+c[j];
                    int sz=g[j].size()-1;
                    for (int s=0;s<=sz;s++) t+=w[g[j][s]];  //r[i]=j
                    //动态数组从0开始记录 
                }
                ans=f[n];  //每次循环的答案都记录在f[n] 
                continue;
            }
            build(1,1,n);  //每次dp就要重构,维护f[j]+cost[j][s];
            for (j=1;j<=n;j++)
            {
                f[j]=ask(1,1,j-1)+c[j];   //询问在j之前的最优状态 
                int sz=g[j].size()-1;  //r==g[j]
                for (int s=0;s<=sz;s++)
                    add(1,1,l[g[j][s]]-1,w[g[j][s]]);  //r[g[j][s]]==j
            }
            ans=min(ans,f[n]);
        } 
        printf("%d",ans);
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        k++;  //在末尾增加一个基站 
        for (int i=2;i<=n;i++) scanf("%d",&d[i]);
        for (int i=1;i<=n;i++) scanf("%d",&c[i]);
        for (int i=1;i<=n;i++) scanf("%d",&s[i]);
        for (int i=1;i<=n;i++) scanf("%d",&w[i]);
        d[++n]=INF;
        w[n]=INF;
        //lower_bound大于等于value 的值
        for (int i=1;i<=n;i++)
        {
            l[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d;
            r[i]=lower_bound(d+1,d+1+n,d[i]+s[i])-d;  //l,r记录的都是编号,至少在l[i]|r[i]建基站i才能被覆盖 
            if (d[r[i]]>d[i]+s[i]) r[i]--;  //计算距离i村庄最近的基站至少要在哪里 
            //这里之所以有这个操作是因为我们用的是lower_bound 
            g[r[i]].push_back(i);  //g[x] r[i]==x的村庄编号 
        }
        doit();
        return 0; 
    }
  • 相关阅读:
    sql server 日期格式化
    DPDK latencystats库使用方案
    PPTP协议握手流程分析--转载
    北京联通IPTV 数码视讯 Q1 破解过程
    Linux发不出分片包的问题分析
    近期团队比较动荡
    ab输出信息解释以及Failed requests原因分析
    salt源码安装
    imuxsock lost 353 messages from pid 20261 due to rate-limiting 解决办法
    解决vue路由history模式刷新后404的问题
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673507.html
Copyright © 2011-2022 走看看