飞翔---------双重线性dp

Posted liu-yi-tong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了飞翔---------双重线性dp相关的知识,希望对你有一定的参考价值。

题目:

鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧。于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中。

这些鹰的起始点被设在一个N*M矩阵的左下角map[1,1]的左下角。终点被设定在矩阵的右上角map[N,M]的右上角,有些map[i,j]是可以从中间穿越的。每一个方格的边长都是100米。如图所示:

技术图片

没有障碍,也没有死路。这样设计主要是为了高速飞行的鹰们不要发现死路来不及调整而发生意外。

潘帕斯雄鹰冒着减RP的危险从比赛承办方戒备森严的基地中偷 来了施工的地图。但是问题也随之而来,他必须在比赛开始之前把地图的每一条路都搞清楚,从中找到一条到达终点最近的路。(哈哈,笨鸟不先飞也要拿冠军)但 是此鹰是前无古鹰,后无来鹰的吃菜长大的鹰--菜鸟。他自己没有办法得出最短的路径,于是紧急之下找到了学OI的你,希望找到你的帮助

 

 

 

刚看到题目好像一脸懵逼,map[ i ][ j ]不应该是一个坐标吗????怎么还可以从中穿过??????

经过半个小时的认真读题后,终于明白了题目的意思,大概如下:

现有N×M个方格子,方格的四周的边是路,鹰只能在路上飞,有一些格子上面有一条斜路,也可以走。

输入:

首行为n,m(0<n,m<=1000000),第2行为k0<k<=1000)表示有多少个特殊的边。以下k行为两个数,i,j表示map[i,j]是可以直接穿越的。

输出:

仅一行,(1,1)-->(n,m)的最短路径的长度,四舍五入保留到整数即可。

思路:

这道题的主要思路有两点,一是求最多特殊路,二是通过特殊路的数量直接求结果。

1.特殊路数量:首先明白一点,要想结果更优,那肯定在去终点m,n的路上走了若干特殊路,在不往回绕的前提下走的特殊路越多,结果越优。

2.推结果:每一跳特殊路相当于代替了两个标准路,所以最后的ans应该=(m+n-2*tot)+tot*sqrt(2)*100;

  (如果一条特殊路都不走,最优路径就是m+n,每走一条,就会少走2条100的路,多走一条100×根号2的路)

好了那么最后问题变成如何求最多的特殊路径了,

显然(好吧,其实我也不知道怎么证),如果一条路径的 i , j 都大于另一条的 i , j 那么这条路经一定可以接在另一条后面,即选了i,j较小的,那么在这种解中,一定可以选> i , > j的一条路径,就类似于一个对角线,直上直下方格中的路径不能同时选,斜对角的可以。

那么,由于题目中路径都是随机给的,我们需要让它变得相对有序,即进行一次排序:

	for(int i=1;i<=k;i++){
		LL x,y;
		scanf("%lld%lld",&x,&y);
		a[++cnt].x=x;
		a[cnt].y=y;
	}
	sort(a+1,a+cnt+1,cmp);
	
	
	bool cmp(FJ a,FJ b){
		if(a.x==b.x)return a.y<b.y;
		else return a.x<b.x;
	}

 尽量使坐标们都成升序排列,然后找双重lis,即a[ i ] . x > a[ j ] . x && a[ i ] . y>a[ j ] . y时才可以转移。

dp代码:

	LL tot=0,xx=0,yy=0;
	for(int i=1;i<=k;i++){
		c[i]=1;
		for(int j=1;j<i;j++)
		if(a[i].x>a[j].x&&a[i].y>a[j].y&&c[i]<c[j]+1){
			c[i]=c[j]+1;
			tot=max(c[i],tot);
		}
	}

 这样就可以求出最大可以走几条特殊路径了,结果也就呼之欲出了。

代码全貌:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 typedef long long LL;
 8 const int maxn=1000000+10;
 9 struct FJ{LL x,y;}a[maxn];
10 LL Max,c[maxn];
11 bool cmp(FJ a,FJ b){
12     if(a.x==b.x)return a.y<b.y;
13     else return a.x<b.x;
14 }
15 int main(){
16     LL n,m;
17     scanf("%lld%lld",&n,&m);
18     LL k;
19     scanf("%lld",&k);
20     int cnt=0;
21     for(int i=1;i<=k;i++){
22         LL x,y;
23         scanf("%lld%lld",&x,&y);
24         a[++cnt].x=x;
25         a[cnt].y=y;
26     }
27     sort(a+1,a+cnt+1,cmp);
28     LL tot=0,xx=0,yy=0;
29     for(int i=1;i<=k;i++){
30         c[i]=1;
31         for(int j=1;j<i;j++)
32         if(a[i].x>a[j].x&&a[i].y>a[j].y&&c[i]<c[j]+1){
33             c[i]=c[j]+1;
34             tot=max(c[i],tot);
35         }
36     }
37     double ans=100*(m+n-2*tot)+100*sqrt(2)*tot;
38     printf("%.0lf",ans);
39     return 0;
40 }

 

以上是关于飞翔---------双重线性dp的主要内容,如果未能解决你的问题,请参考以下文章

如何通过单击片段内的线性布局从片段类开始新活动?下面是我的代码,但这不起作用

POJ1185炮兵阵地(状态压缩DP)

如何在android中的地图片段内中心线性布局?

小代码   飞翔的小鸟

在第6731次释放指针后双重免费或损坏

NYOJ17 最长单调递增子序列 线性dp