问题
分析
这道题和之前的修缮长城有点相似
区别是范围时间损失被简化了,但是可以选择一些顾客不送,也就是跳过他们,这就引入了一个问题,那就是没办法将损失累积到一个顾客上面(因为不知道到底要服务多少个顾客),然后将时间归0,所以在dp[i][j][2]的基础上增加一个维度,用来表示服务的顾客数量(有一些顾客被放弃了),然后我们就可以按照修缮长城的做法了
dp[i][j][k][pos] 已经处理了[i,j]顾客(不管送还是没送),现在在位置pos(0代表i,1代表j),还要送剩余k个顾客时,剩下k个顾客能给的最大收入
状态转移方程: 可以选择左边的点或者右边的点
左边的点i:
右边的点j:
*cnt(p[pos]-p[i])**就是损失的累计,我们必须先知道cnt才能计算它,所以要遍历for(int k=1;k<=n;++k) cnt这个数的值,才能累计损失在一个点上,将时间归为0,方便求解
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
using namespace std;
const int maxn=100+5; //客人总数
int p[maxn],e[maxn]; //position,earn
//dp[i][j][k][p] 已经处理了[i,j]顾客(不管送还是没送),现在在位置p(0代表i,1代表j),还要送剩余k个顾客时,剩下k个顾客能给的最大收入
int dp[maxn][maxn][maxn][2],vis[maxn][maxn][maxn][2],kase=1,T,n;
int DP(int left,int right,int cnt,int pos){
if(cnt==0) return 0;
int &ans=dp[left][right][cnt][pos];
if(vis[left][right][cnt][pos]==kase) return ans;
vis[left][right][cnt][pos]=kase;
ans=0;
if(pos){ //人位于right
for(int i=0;i<left;++i){ //有一些人被跳过了,不处理,下一个处理左边的i
ans=max(ans,e[i]-cnt*(p[right]-p[i])+DP(i,right,cnt-1,0));
}
for(int i=right+1;i<n;++i){ //下一个处理右边的人,但有可能跳过right+1
ans=max(ans,e[i]-cnt*(p[i]-p[right])+DP(left,i,cnt-1,1));
}
}else{ //人位于left
for(int i=0;i<left;++i){
ans=max(ans,e[i]-cnt*(p[left]-p[i])+DP(i,right,cnt-1,0));
}
for(int i=right+1;i<n;++i){
ans=max(ans,e[i]-cnt*(p[i]-p[left])+DP(left,i,cnt-1,1));
}
}
return ans;
}
int main(void){
cin>>T;
while(kase<=T){
cin>>n;
for(int i=0;i<n;++i) scanf("%d",&p[i]);
for(int i=0;i<n;++i) scanf("%d",&e[i]);
int ans=0; //ans=0可以看作最差一个人也不送
//处理有的客人不送时,方法比较巧妙
for(int k=1;k<=n;++k){ //只送给k个客人,其他的客人不送
for(int i=0;i<n;++i){
ans=max(ans,e[i]-k*abs(p[i])+DP(i,i,k-1,0)); //只送给k个客人,第i个客人先被送餐,在它上面累计这段时间的损失
}
}
printf("%d\n",ans);
++kase;
}
}