ZJOI2010]基站选址
NOI/NOI+/CTSC 胆小勿入
一、题目及数据范围
有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。
40%的数据中,N<=500;
100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。
二、暴力做法
这题一看就是dp(暴力也得不了分),设计dp[i][j]为前i个点均被覆盖,建了j个基站,且最后一个基站被建立在i点的最小花费
则dp[i][j]=min(dp[i][j],dp[k][j-1]+cost+c[i]),cost为k到i中没被覆盖的点的w之和。
现在的难点在于求cost。我们可以让k从i-1递减,定义l[x]和r[x]为k点能被覆盖区间的左端点和右端点,则当r[x]<i时,说明它不能被i点覆盖,只能被在l[x]到x的范围被覆盖(我们此时的k在x点),所以小于l[x]-1的k点要额外支付w[x]的费用,我们可以O(n)的时间处理每个点的cost信息,时间复杂度O(n^2*k),得分50,附上代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 2e9+5
#define min(x,y) ((x)<(y)?(x):(y))
const int MAXN = 20005, MAXK = 105;
int n,k,d[MAXN],c[MAXN],s[MAXN],w[MAXN],l[MAXN],r[MAXN];
int dp[MAXN][MAXK],cost[MAXN],ans;
int main()
{
scanf("%d %d",&n,&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]);
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;
r[i]-=(d[i]+s[i]<d[r[i]]);
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=k; j++)
dp[i][j]=INF;
dp[i][0]=dp[i-1][0]+w[i];
}
ans=dp[n][0];
for(int i=1; i<=n; i++)
{
memset(cost,0,sizeof cost);
for(int j=i-1; j>=1; j--) //这里的j就是上文的k,遍历所有k处理出cost
{
if(r[j]<i)
cost[l[j]-1]+=w[j];
}
for(int j=i-1; j>=0; j--)
cost[j]+=cost[j+1];//用后缀和统计w和
for(int j=1; j<=k && j<=i; j++) //基站数量
{
if(j==1)
dp[i][1]=cost[0]+c[i];
else
for(int p=i-1; p>=j-1; p--) //枚举k点
dp[i][j]=min(dp[i][j],dp[p][j-1]+cost[p]+c[i]);