(COCI 2017/2018 7) Go
· 阅读需 5 分钟
题解
题意
有一条长度为 N 的街道,房子从左到右编号为 1 到 N。
有 M 只小精灵,第 i 只位于房子 Aᵢ,价值 Bᵢ 颗糖果,且必须在 Tᵢ 秒内被抓住(从比赛开始计时,到第 Tᵢ 秒结束前抓住都算),否则消失。每只小精灵位于不同的房子,且 Aᵢ 严格递增给出。
Branimirko 从第 1 秒开始位于房子 K。每一秒他可以:
- 向左或向右移动到相邻房子(耗时 1 秒),或
- 停留在原地(耗时 1 秒)。
当他在某一秒结束时与某只小精灵位于同一房子,且该小精灵尚未消失,则抓住它并获得其糖果。
求他能获得的最大糖果总数。
数据范围
- 1 ≤ K ≤ N ≤ 10³
- 1 ≤ M ≤ 100
- 1 ≤ Aᵢ ≤ N,严格递增
- 1 ≤ Bᵢ ≤ 100
- 1 ≤ Tᵢ ≤ 2000
- 对于 20% 的数据:M ≤ 10
- 另外 20% 的数据:M ≤ 20
解法
很明显能看出来需要动态规划。
但是问题在于如何设计状态和转移。
这个题有一个很大的迷惑点在于 于是有一个看起来很显然的状态就被设计出来了表示当前在门牌号,时间为得到的最大糖果数。
但是这样设计转移就会出问题。
比如如何保证某个位置的糖果只取一次?
如果继续沿着这方面细想就会走上歧路,或者得出非常麻烦的做法。
这个时候我们就要换一个方向思考,我们发现我们在没有小精灵的房子进行折返一定是不优的,这只会白白浪费时间,所以我们的移动一定是从某个小精灵的房子(或起点)径直走向另一个有小精灵的房子。而题目保证了单增,我们不需要排序就能轻松求出有小精灵的房子之间的距离。
然后我们发现,无论我们如何往返,我们一定是在一个区间内行走。并且最后我们一定在这个区间的一个端点上。那么我们可以设计状态表示在这个区间上,已经过了秒,p为表示当前在左/右端点上。
转移时要注意当前是否超时,超时了不会增加权值,但也有可能比别的方案更优,仍需把状态转移过来。
转移是麻烦而显然的,详细可以看代码。时间复杂度为
代码
code
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e2+10;
const ll inf=1e18;
int n,k,m;
ll f[N][N][2020][2];
ll b[N],a[N],t[N];
int s;
int main()
{
// freopen("go.in.1","r",stdin);
// freopen("4.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>k>>m;
bool flag=false,flag2=false;
for(int i=1;i<=m;i++)
{
if(!flag)
{
cin>>b[i]>>a[i]>>t[i];
if(b[i]==k) flag2=true,s=i;
if(b[i]>k&&(!flag2))
{
flag=true;
b[i+1]=b[i];
a[i+1]=a[i];
t[i+1]=t[i];
b[i]=k;
a[i]=0;
t[i]=0;
s=i;
}
}
else cin>>b[i+1]>>a[i+1]>>t[i+1];
}
if(!flag&&!flag2) b[++m]=k,s=m;
if(flag) m++;
// for(int i=1;i<=m;i++) cout<<b[i]<<' ';
memset(f,0xcf,sizeof f);
f[s][s][0][0]=f[s][s][0][1]=a[s];
ll ans=0;
for(int len=1;len<=m;len++)
{
for(int l=1;l+len-1<=m;l++)
{
int r=l+len-1;
for(int T=0;T<=2000;T++)
{
if(T-(b[l+1]-b[l])>=0) f[l][r][T][0]=max(f[l][r][T][0],f[l+1][r][T-(b[l+1]-b[l])][0]+(T<t[l])*a[l]);
if(T-(b[r]-b[l])>=0) f[l][r][T][0]=max(f[l][r][T][0],f[l+1][r][T-(b[r]-b[l])][1]+(T<t[l])*a[l]);
if(T-(b[r]-b[r-1])>=0) f[l][r][T][1]=max(f[l][r][T][1],f[l][r-1][T-(b[r]-b[r-1])][1]+(T<t[r])*a[r]);
if(T-(b[r]-b[l])>=0) f[l][r][T][1]=max(f[l][r][T][1],f[l][r-1][T-(b[r]-b[l])][0]+(T<t[r])*a[r]);
ans=max(ans,f[l][r][T][0]);
ans=max(ans,f[l][r][T][1]);
}
}
}
cout<<ans;
return 0;
}