UVA1336 Fixing the Great Wall && 洛谷P2466 [SDOI2008]Sue的小球【区间DP&&记忆化搜索】

UVA1336 Fixing the Great Wall

Time limit 3000ms
在这里插入图片描述


题目大意

长城上有N个地点要修补,给定每个点的位置、初始修复成本,每单位时间增加的成本
给定修补机器人 初始位置及移动速度
求机器人如何移动使得总成本最小


题目分析

将这N个点按位置排序
显然如果一个点走过他再回头修一定不会最优
所以任意时刻已修补的区间一定是连续的
即若当前以修好第 l l ll ll~ r r rr rr个地点,那么下一个修补的一定是 l l − 1 ll-1 ll1 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[ll1])及计算剩余没修补的地点每单位时间增加的成本总和
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[ll1][rr][0]+Len(ll1,ll)/VW,dp[ll][rr+1][1]+Len(ll,r+1)/VW)
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[ll1][rr][0]+Len(ll1,rr)/VW,dp[ll][rr+1][1]+Len(rr,r+1)/VW)

注意这里我们还要把机器人的初始位置作为一个点加进去,所以总点数应为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 &lt; = x i , y i , v i &lt; = 1 0 4 , N &lt; = 1000 -10^4 &lt;= xi,yi,vi &lt;= 10^4,N &lt; = 1000 104<=xi,yi,vi<=104N<=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=(SUMDP(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;
}

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值