zoukankan      html  css  js  c++  java
  • [洛谷P2605] ZJOI2016 基站选址

    问题描述

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

    输入格式

    输入文件的第一行包含两个整数N,K,含义如上所述。

    第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。

    第三行包含N个整数,表示C1,C2,…CN。

    第四行包含N个整数,表示S1,S2,…,SN。

    第五行包含N个整数,表示W1,W2,…,WN。

    输出格式

    输出文件中仅包含一个整数,表示最小的总费用。

    样例输入

    3 2
    1 2
    2 3 2
    1 1 0
    10 20 30

    样例输出

    4

    说明

    40%的数据中,N<=500;

    100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。

    解析

    可以想到是动态规划的题目。设(f[i][j])表示把第j个基站修在第i个村庄且不考虑第i+1到第n个村庄的最小费用。那么,状态转移方程为

    [f[i][j]=min(f[k][j-1]+cost(k,i)) ]

    其中(cost(k,i))表示(k)(i)的村庄中没有被(i)(k)基站覆盖到的村庄所需的赔偿费用。这样做是可能会达到(O(n^2k))的复杂度。考虑如何优化。

    对每一个村庄(i)求出最左边能够覆盖到它的村庄和最右边能够覆盖到它的村庄(记为(l[i])(r[i]))。那么在(l[i])之前的村庄都无法覆盖到(i)。在往(i+1)转移的时候,对于村庄(k)满足(r[k]=i),如果是从([1,l[k]-1])转移过来的话,一定会赔偿(k),即([1,l[k]-1])的费用都会增加(k)的赔偿。所以,用线段树维护(f[k][j-1]+cost(k,i))的值,同时用邻接表记录(r[k]=i)(k),每次转移都区间修改,然后在([1,i-1])中取最小值。

    还有一点,由于DP时没有考虑后面的村庄,为了避免漏掉最后一个村庄的情况,我们可以加入第(n+1)个村庄,并强制这个村庄上建立第(k+1)个基站。

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define N 20002
    using namespace std;
    const int inf=1<<30;
    struct SegmentTree{
    	int dat,add;
    }t[N*4];
    int head[N],ver[N*2],nxt[N*2],tot;
    int n,k,i,j,x,d[N],c[N],s[N],w[N],l[N],r[N],f[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y)
    {
    	tot++;
    	ver[tot]=y;
    	nxt[tot]=head[x];
    	head[x]=tot;
    }
    void build(int p,int l,int r)
    {
    	t[p].add=t[p].dat=0;
    	if(l==r){
    		t[p].dat=f[l];
    		return;
    	}
    	int mid=(l+r)/2;
    	build(p*2,l,mid);
    	build(p*2+1,mid+1,r);
    	t[p].dat=min(t[p*2].dat,t[p*2+1].dat);
    }
    void spread(int p)
    {
    	if(t[p].add){
    		t[p*2].dat+=t[p].add;
    		t[p*2+1].dat+=t[p].add;
    		t[p*2].add+=t[p].add;
    		t[p*2+1].add+=t[p].add;
    		t[p].add=0;
    	}
    }
    void change(int p,int l,int r,int ql,int qr,int x)
    {
    	if(ql>qr) return;
    	if(ql<=l&&r<=qr){
    		t[p].dat+=x;
    		t[p].add+=x;
    		return;
    	}
    	int mid=(l+r)/2;
    	spread(p);
    	if(ql<=mid) change(p*2,l,mid,ql,qr,x);
    	if(qr>mid) change(p*2+1,mid+1,r,ql,qr,x);
    	t[p].dat=min(t[p*2].dat,t[p*2+1].dat);
    }
    int ask(int p,int l,int r,int ql,int qr)
    {
    	if(ql>qr) return 0;
    	if(ql<=l&&r<=qr) return t[p].dat;
    	int mid=(l+r)/2,ans=1<<30;
    	spread(p);
    	if(ql<=mid) ans=min(ans,ask(p*2,l,mid,ql,qr));
    	if(qr>mid) ans=min(ans,ask(p*2+1,mid+1,r,ql,qr));
    	return ans;
    }
    int main()
    {
    	n=read(),k=read();
    	for(i=2;i<=n;i++) d[i]=read();
    	for(i=1;i<=n;i++) c[i]=read();
    	for(i=1;i<=n;i++) s[i]=read();
    	for(i=1;i<=n;i++) w[i]=read();
    	n++;k++;
    	d[n]=w[n]=inf;
    	for(i=1;i<=n;i++){
    		l[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
    		r[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
    		if(d[r[i]]>d[i]+s[i]) r[i]--;
    		insert(r[i],i);
    	}
    	int ans=inf;
    	for(j=1;j<=k;j++){
    		if(j==1){
    			int tmp=0;
    			for(i=1;i<=n;i++){
    				f[i]=tmp+c[i];
    				for(x=head[i];x;x=nxt[x]) tmp+=w[ver[x]];
    			}
    		}
    		else{
    			build(1,1,n);
    			for(i=1;i<=n;i++){
    				f[i]=ask(1,1,n,j-1,i-1)+c[i];
    				for(x=head[i];x;x=nxt[x]) change(1,1,n,1,l[ver[x]]-1,w[ver[x]]);
    			}
    		}
    		ans=min(ans,f[n]);
    	}
    	printf("%d
    ",ans);
    }
    
  • 相关阅读:
    bzoj3272 Zgg吃东西
    bzoj3894 文理分科
    poj1149 PIGS
    poj1637 Sightseeing tour
    [Wc2007]剪刀石头布
    poj2396 Budget
    [NOI2017]游戏
    CF666E Forensic Examination
    bzoj4889 [Tjoi2017]不勤劳的图书管理员
    CF587F Duff is Mad
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11878642.html
Copyright © 2011-2022 走看看