P2466 [SDOI2008] Sue 的小球
题解
我们发现,当前的决策会影响未来的结果,因此我们把当前的决策存下来,这样等未来要用的时候就有的转移了,如果未来由多个状态决定,那就现在把那些状态都记录下来
我们发现,一个点如果能被吸收,那么其左边或右边的一个区间的点都肯定被吸收了,所以我们记录 \(f[i][j]\) 表示区间 \([i,j]\) 的分数,
但是一个区间被遍历完时,最后一个遍历到的点可能在左边或者右边,所以我们再增加一维状态来表示
如果正着计算,那么不仅要存储当前状态的分数,还要存储当前状态的时间,而不同的时间和分数搭配对未来会产生不同的影响,因为分数不是越大越好,时间也不是越短越好,很复杂
不如换个角度来考虑问题,我们令 \(f[i][j][0/1]\) 来表示这个区间遍历完时,累积的失去的分数,这样一来,只需要贪心失去的分数越小越好
再有一个疑问:为什么累积扣分最少一定最优?
因为从当前区间向外扩展时扣掉的分数是不会变的,为了使 \([1,n]\) 扣分最少,其前面一个区间扣分也要最少,所以累积扣分最少最优
ps:讲题水平还是不行啊
code
#define ll long long
#include<bits/stdc++.h>
using namespace std;
inline void read(ll &x) {
x = 0;
ll flag = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')flag = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
x *= flag;
}
inline void write(ll x)
{
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
ll n,x0;
struct unit
{
ll x,y,v;
}ball[1005];
bool cmp(unit a,unit b)
{
return a.x<b.x;
}
ll pres[1005]={0};
ll dp[1005][1005][2]={0};
int main()
{
read(n); read(x0);
ball[n+1].x=x0;//很机智的一个做法:把初始点加进去,y和v都为0,相当于出发点
ll sum=0;
for(ll i=1;i<=n;i++) read(ball[i].x);
for(ll i=1;i<=n;i++) read(ball[i].y),sum+=ball[i].y;
for(ll i=1;i<=n;i++) read(ball[i].v);
n++;//注意
sort(ball+1,ball+n+1,cmp);
for(ll i=1;i<=n;i++) pres[i]=pres[i-1]+ball[i].v;
memset(dp,0x3f,sizeof dp);
ll i;
for(i=1;i<=n;i++)
if(ball[i].x==x0)
{
dp[i][i][0]=dp[i][i][1]=0;
break;
}
for(ll len=2;len<=n;len++)
{
for(ll l=1,r=len;r<=n;l++,r++)
{
dp[l][r][0]=min(dp[l+1][r][0]+(ball[l+1].x-ball[l].x)*(pres[n]-pres[r]+pres[l]),dp[l+1][r][1]+(ball[r].x-ball[l].x)*(pres[n]-pres[r]+pres[l]));
dp[l][r][1]=min(dp[l][r-1][0]+(ball[r].x-ball[l].x)*(pres[n]-pres[r-1]+pres[l-1]),dp[l][r-1][1]+(ball[r].x-ball[r-1].x)*(pres[n]-pres[r-1]+pres[l-1]));
//思维反转,不计算这么走能得到的分数,而是计算这么走会失去多少分数
}
}
printf("%.3lf",(sum-min(dp[1][n][0],dp[1][n][1]))*1.0/1000);
return 0;
}

浙公网安备 33010602011771号