【问题描述】
你得到了一个龙珠雷达,它会告诉你龙珠出现的时间和地点。
龙珠雷达的画面是一条水平的数轴,每一个窗口时间,数轴的某些点上会出现同一种龙珠,每当你获得其中一颗龙珠,其它龙珠就会消失。下一个窗口时间,数轴上又会出现另一种龙珠。总共有n个窗口时间,也就是总共有n种龙珠。
假设你会瞬间移动,你从数轴的x点移动到y点,耗时0秒,但是需要耗费|x-y|的体力。同时,挖出一颗龙珠也需要耗费一定的体力。请问,最少耗费多少体力,就可以收集齐所有种类的龙珠。
【输入格式】
第一行,三个整数n,m,x,表示共有n个窗口时间,每个窗口时间会出现m个龙珠,x是一开始你所处的位置。
接下来有两个n*m的矩阵。
对于第一个矩阵,坐标为(i,j)的数字表示第i个窗口时间,第j个龙珠的位置。
对于第二个矩阵,坐标为(i,j)的数字表示第i个窗口时间,挖取第j个龙珠所需的体力。
【输出格式】
一个整数,表示所需最小体力
【输入样例】
3 2 5
2 3
4 1
1 3
1 1
1 3
4 2
【输出样例】
8
【数据范围】
所有数据均为整数
数轴范围在0到30000
挖一颗龙珠所需体力不超过30000
结果保证在int范围
对于50%的数据,1<=n<=50,1<=m<=500。
对于100%的数据,1<=n<=50,1<=m<=5000。
不难分析出动态规划方程:
设结构体:a[i][j].x=表示第 i 个时间窗口出现的第 j 个龙珠的位置。
:a[i][j].c=表示第 i 个时间窗口挖第 j 个龙珠需要耗费的体力。
//f(i,j)表示在第i个窗口拾取第j个龙珠后所耗费的最小体力
//f(i,j)=min(f(i-1,k)+abs(a[i-1][k].wz-a[i][j].wz) || 1<=k<=m )+a[i][j].c
边界:
f(1,j)= |a[1][j].x-x0|+ a[1][j].c //在第 1 个时间窗口收集第 j 个龙珠需要的体力
纯粹dp:
solve50()
for(int j=1;j<=m;j++)
f[1][j]=abs(a[1][j].wz-p)+a[1][j].c;
//f(i,j)表示在第i个窗口拾取第j个龙珠后所耗费的最小体力
//f(i,j)=min( f(i-1,k)+abs(p[i-1][k].wz-p[i][j].wz) || 1<=k<=m )+p[i][j].c
for(int i=2;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ans=inf;
for(int k=1;k<=m;k++)
{
rr=f[i-1][k]+abs(a[i-1][k].wz-a[i][j].wz);
ans=min(ans,rr);
}
f[i][j]=ans+a[i][j].c;
}
}
ans=inf;
for(int i=1;i<=m;i++)
ans=min(ans,f[n][i]);
printf("%d",ans);
加入单调队列优化(偷个懒,因为这个滑动窗口左端固定,不用记录下标,只找最小值):
solve100()
for(int i=2;i<=n;i++)
{
int k=1;
int minv=inf;
//when(p[i][j].wz>=p[i-1][k].wz)
//f(i,j)=min(f(i-1,k)-p[i-1][k].wz) || 1<=k<=m )+p[i][j].c+p[i][j].wz
for(int j=1;j<=m;j++)
{
while(k<=m && a[i][j].wz>=a[i-1][k].wz)
{
t=f[i-1][k]-a[i-1][k].wz;
minv=min(minv,t);
k++;
}
f[i][j]=minv+a[i][j].c+a[i][j].wz;
}
k=m,minv=inf;
//when(p[i][j].wz<p[i-1][k].wz)
//f(i,j)=min(f(i-1,k)+p[i-1][k].wz) || 1<=k<=m )+p[i][j].c-p[i][j].wz
for(int j=m;j>=1;j--)
{
while(k>=1 && a[i][j].wz<a[i-1][k].wz)
{
t=f[i-1][k]+a[i-1][k].wz;
minv=min(minv,t);
k--;
}
f[i][j]=min(f[i][j],minv+a[i][j].c-a[i][j].wz);
}
}
ans=inf;
for(int i=1;i<=m;i++)
ans=min(ans,f[n][i]);
printf("%d",ans);