UVA1336 Fixing the Great Wall
Time limit 3000ms
题目大意
长城上有N个地点要修补,给定每个点的位置、初始修复成本,每单位时间增加的成本
给定修补机器人 初始位置及移动速度
求机器人如何移动使得总成本最小
题目分析
将这N个点按位置排序
显然如果一个点走过他再回头修一定不会最优
所以任意时刻已修补的区间一定是连续的
即若当前以修好第
l
l
ll
ll~
r
r
rr
rr个地点,那么下一个修补的一定是
l
l
−
1
ll-1
ll−1或
r
r
+
1
rr+1
rr+1
定义
d
p
[
l
l
]
[
r
r
]
[
0
/
1
]
dp[ll][rr][0/1]
dp[ll][rr][0/1]表示已经修好了
[
l
l
,
r
r
]
[ll,rr]
[ll,rr]区间的地点,且当前所在位置为
l
l
(
0
)
ll(0)
ll(0)还是
r
r
(
1
)
rr(1)
rr(1)
修补完剩下的地点所需最小化费
初始化
d
p
[
1
]
[
n
]
[
0
]
=
d
p
[
1
]
[
n
]
[
1
]
=
0
dp[1][n][0]=dp[1][n][1]=0
dp[1][n][0]=dp[1][n][1]=0,其余为INF
状态转移方程
令
W
=
S
u
m
[
n
]
−
(
S
u
m
[
r
r
]
−
S
u
m
[
l
l
−
1
]
)
W=Sum[n]-(Sum[rr]-Sum[ll-1])
W=Sum[n]−(Sum[rr]−Sum[ll−1])及计算剩余没修补的地点每单位时间增加的成本总和
d
p
[
l
l
]
[
r
r
]
[
0
]
=
m
i
n
(
d
p
[
l
l
−
1
]
[
r
r
]
[
0
]
+
L
e
n
(
l
l
−
1
,
l
l
)
/
V
∗
W
,
d
p
[
l
l
]
[
r
r
+
1
]
[
1
]
+
L
e
n
(
l
l
,
r
+
1
)
/
V
∗
W
)
dp[ll][rr][0]=min(dp[ll-1][rr][0]+Len(ll-1,ll)/V*W,dp[ll][rr+1][1]+Len(ll,r+1)/V*W)
dp[ll][rr][0]=min(dp[ll−1][rr][0]+Len(ll−1,ll)/V∗W,dp[ll][rr+1][1]+Len(ll,r+1)/V∗W)
d
p
[
l
l
]
[
r
r
]
[
1
]
=
m
i
n
(
d
p
[
l
l
−
1
]
[
r
r
]
[
0
]
+
L
e
n
(
l
l
−
1
,
r
r
)
/
V
∗
W
,
d
p
[
l
l
]
[
r
r
+
1
]
[
1
]
+
L
e
n
(
r
r
,
r
+
1
)
/
V
∗
W
)
dp[ll][rr][1]=min(dp[ll-1][rr][0]+Len(ll-1,rr)/V*W,dp[ll][rr+1][1]+Len(rr,r+1)/V*W)
dp[ll][rr][1]=min(dp[ll−1][rr][0]+Len(ll−1,rr)/V∗W,dp[ll][rr+1][1]+Len(rr,r+1)/V∗W)
注意这里我们还要把机器人的初始位置作为一个点加进去,所以总点数应为N+1
设排序后表示机器人的点为K,从DP(k,k,0)开始记搜即可
答案为
d
p
[
k
]
[
k
]
[
0
]
+
S
U
M
dp[k][k][0]+SUM
dp[k][k][0]+SUM (SUM为初始成本总和)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef double dd;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int inf=1e9;
const int maxn=1010;
int n,v,x;
struct node{dd pos,c,dt;}rem[maxn];
bool cmp(node a,node b){return a.pos<b.pos;}
dd sum[maxn],dp[maxn][maxn][2];
dd DP(int ll,int rr,int d)
{
if(ll==1&&rr==n+1) return 0;
if(dp[ll][rr][d]!=inf) return dp[ll][rr][d];
dd resl=0,resr=0,ss=sum[n+1]-(sum[rr]-sum[ll-1]);
if(ll>1){
if(d==1) resl=(rem[rr].pos-rem[ll-1].pos)/v*ss;
else resl=(rem[ll].pos-rem[ll-1].pos)/v*ss;
}
if(rr<=n){
if(d==1) resr=(rem[rr+1].pos-rem[rr].pos)/v*ss;
else resr=(rem[rr+1].pos-rem[ll].pos)/v*ss;
}
if(ll>1) dp[ll][rr][d]=min(dp[ll][rr][d],DP(ll-1,rr,0)+resl);
if(rr<=n) dp[ll][rr][d]=min(dp[ll][rr][d],DP(ll,rr+1,1)+resr);
return dp[ll][rr][d];
}
int main()
{
while(scanf("%d%d%d",&n,&v,&x)!=EOF)
{
if(n==0&&v==0&&x==0) break; dd ssum=0;
for(int i=1;i<=n;++i)
scanf("%lf%lf%lf",&rem[i].pos,&rem[i].c,&rem[i].dt);
rem[n+1].pos=x; rem[n+1].c=0; rem[n+1].dt=0;//将机器人也作为一个点加入
sort(rem+1,rem+2+n,cmp);
for(int i=1;i<=n+1;++i)
ssum+=rem[i].c,
sum[i]=sum[i-1]+rem[i].dt;
for(int i=1;i<=n+1;++i)
for(int j=1;j<=n+1;++j)
dp[i][j][0]=dp[i][j][1]=inf;
for(int i=1;i<=n+1;++i)
if(rem[i].pos==x){ printf("%.0lf\n",floor(DP(i,i,0)+ssum)); break;}
}
return 0;
}
洛谷P2466 [SDOI2008]Sue的小球
时空限制 1000ms / 128MB
题目描述
Sue和Sandy最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue有一支轻便小巧的小船。然而,Sue的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响Sue的兴趣,因为每一个彩蛋都是不同的,Sue希望收集到所有的彩蛋。
然而Sandy就没有Sue那么浪漫了,Sandy希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型:
以Sue的初始位置所在水平面作为x轴。
一开始空中有N个彩蛋,对于第i个彩蛋,他的初始位置用整数坐标(xi, yi)表示,游戏开始后,它匀速沿y轴负方向下落,速度为vi单位距离/单位时间。Sue的初始位置为(x0, 0),Sue可以沿x轴的正方向或负方向移动,Sue的移动速度是1单位距离/单位时间,使用秘密武器得到一个彩蛋是瞬间的,得分为当前彩蛋的y坐标的千分之一。
现在,Sue和Sandy请你来帮忙,为了满足Sue和Sandy各自的目标,你决定在收集到所有彩蛋的基础上,得到的分数最高。
输入格式:
第一行为两个整数N, x0用一个空格分隔,表示彩蛋个数与Sue的初始位置。
第二行为N个整数xi,每两个数用一个空格分隔,第i个数表示第i个彩蛋的初始横坐标。
第三行为N个整数yi,每两个数用一个空格分隔,第i个数表示第i个彩蛋的初始纵坐标。
第四行为N个整数vi,每两个数用一个空格分隔,第i个数表示第i个彩蛋匀速沿y轴负方向下落的的速度。
输出格式:
一个实数,保留三位小数,为收集所有彩蛋的基础上,可以得到最高的分数。
说明
对于30%的数据,N<=20。
对于60%的数据,N<=100。
对于100%的数据,
−
1
0
4
<
=
x
i
,
y
i
,
v
i
<
=
1
0
4
,
N
<
=
1000
-10^4 <= xi,yi,vi <= 10^4,N < = 1000
−104<=xi,yi,vi<=104,N<=1000
题目分析
和上面一样的题型
每个彩蛋
i
i
i的得分看作 初始从坐标
y
i
−
y_i-
yi−下落距离
为了让这个得分尽可能的大,转换一下就是要让下落距离尽量小
令SUM为初始所有纵坐标总和,
s
u
m
[
i
]
sum[i]
sum[i]记录
v
i
v_i
vi前缀和
d
p
[
l
l
]
[
r
r
]
[
0
/
1
]
dp[ll][rr][0/1]
dp[ll][rr][0/1]表示已经取得了第
l
l
ll
ll~
r
r
rr
rr的彩蛋且当前在
l
l
ll
ll还是
r
r
rr
rr,要取完剩下区间能得到的最小下落距离
和上面的转移如出一辙
记得把初始位置也作为一个点加进去,
x
i
xi
xi升序排序后记搜即可
最后
a
n
s
=
(
S
U
M
−
D
P
(
K
,
K
,
0
)
)
/
1000
ans=(SUM-DP(K,K,0))/1000
ans=(SUM−DP(K,K,0))/1000
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cmath>
using namespace std;
typedef long long lt;
typedef double dd;
int read()
{
int x=0,f=1;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
const int inf=1128481603;
const int maxn=1010;
int n,x0,SUM;
struct node{ int xi,yi,vi,f; node(){xi=yi=vi=f=0;}}rem[maxn];
bool cmp(node a,node b){return a.xi<b.xi;}
int sum[maxn],dp[maxn][maxn][2];
int DP(int ll,int rr,int d)
{
if(ll==1&&rr==n) return 0;
if(dp[ll][rr][d]!=inf) return dp[ll][rr][d];
int res=inf,resl=0,resr=0,ss=sum[n]-(sum[rr]-sum[ll-1]);
if(ll>1){
if(d==1) resl=(rem[rr].xi-rem[ll-1].xi)*ss;
else resl=(rem[ll].xi-rem[ll-1].xi)*ss;
}
if(rr<n){
if(d==1) resr=(rem[rr+1].xi-rem[rr].xi)*ss;
else resr=(rem[rr+1].xi-rem[ll].xi)*ss;
}
if(ll>1) res=min(res,resl+DP(ll-1,rr,0));
if(rr<n) res=min(res,resr+DP(ll,rr+1,1));
return dp[ll][rr][d]=res;
}
int main()
{
n=read(); x0=read();
for(int i=1;i<=n;++i) rem[i].xi=read();
for(int i=1;i<=n;++i) rem[i].yi=read();
for(int i=1;i<=n;++i) rem[i].vi=read();
rem[++n].xi=x0; rem[n].yi=0; rem[n].vi=0; rem[n].f=1;
memset(dp,67,sizeof(dp));
sort(rem+1,rem+n+1,cmp);
for(int i=1;i<=n;++i)
sum[i]=sum[i-1]+rem[i].vi,SUM+=rem[i].yi;
for(int i=1;i<=n;++i)
if(rem[i].f==1){ printf("%.3lf",(dd)(SUM-DP(i,i,1))/1000.0); break;}
return 0;
}