HAWK(一道隐秘的最长上升子序列题)
Description
鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧。于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中。
这些鹰的起始点被设在一个N*M矩阵的左下角map[1,1]的左下角。终点被设定在矩阵的右上角map[N,M]的右上角,有些map[i,j]是可以从中间穿越的。每一个方格的边长都是100米。如图所示:
没有障碍,也没有死路。这样设计主要是为了高速飞行的鹰们不要发现死路来不及调整而发生意外。潘帕斯雄鹰冒着减RP的危险从比赛承办方戒备森严的基地中偷来了施工的地图。但是问题也随之而来,他必须在比赛开始之前把地图的每一条路都搞清楚,从中找到一条到达终点最近的路。
但是此鹰是前无古鹰,后无来鹰的吃菜长大的鹰--菜鸟。他自己没有办法得出最短的路径,于是紧急之下找到了学OI的你,希望找到你的帮助。
Input
首行为n,m(0< n,m< =100000),
第2行为k(0< k< =1000)表示有多少个特殊的边。
以下k行为两个数,i,j表示map[i,j]是可以直接穿越的。
Output
仅一行,1,1--> n,m的最短路径的长度,四舍五入保留到整数即可.
Sample Input
3 2
3
1 1
3 2
1 2
Sample Output
383
剖析
不难看出,要想达到终点路径最短,需要这一条路径中斜边尽可能得多(到达相同的效果,走斜边需要根号二*100的代价,而不走斜边需要200的代价)。
若是没有斜边存在,显然从左下角走到右上角的代价是固定的,都是(n+m)*100,所以最终答案可以是这个代价减去因为走斜边而减少的代价。
那么,如何尽可能地多走斜边呢?
想要从左下角走到右上角而路程代价最小,再笨的eagle也绝不会走回头路,也就是说,它只会向右上走。这时思路其实已经很明晰了:这时一个最长上升子序列问题。我们要做的是将每一条斜边都按横坐标排序,来保证eagle一直向右走,然后对于纵坐标求最长上升子序列的长度,就是在节约代价的条件下能够走到的最多的斜边。
将问题简化成一个最长上升子序列问题后,就可以愉快的用线性dp将这道题AC了。
代码:
//由于这道题数据很水,这里用的是n方代价的线性dp。
#include<bits/stdc++.h>//CindyMarshall using namespace std; #define maxn 100010 int n,m,k,tmp; struct la{ int x;int y; }w[maxn]; int f[maxn]; bool operator < (la a,la b){ return a.x<b.x; } int main() { scanf("%d%d",&n,&m); scanf("%d",&k); for(int i=1;i<=k;i++){ scanf("%d%d",&w[i].x,&w[i].y); } sort(w+1,w+k+1); for(int i=1;i<=k;i++){ f[i]=1; for(int j=1;j<i;j++){ if(w[i].y>w[j].y){ f[i]=max(f[i],f[j]+1); if(f[i]>tmp) tmp=f[i]; } } } double t=(n+m)*100+sqrt(2)*tmp*100-200*tmp; int ans=(n+m)*100+sqrt(2)*tmp*100-200*tmp; if(t-ans>=0.5) ans++; printf("%d",ans); }