Codevs5288 航线设计(动态规划加强版) 解题报告

【问题描述】  
  
  有一个国家被一条河划分为南北两部分,在南岸和北岸总共有N对城镇,每一城镇在对岸都有唯一的友好城镇。任何两个城镇都没有相同的友好城镇。每一对友好城镇都希望有一条航线来往。于是他们向政府提出了申请。由于河终年有雾。政府决定不允许有任两条航线交叉(如果两条航线交叉,将有很大机会撞船)。


  你的任务是写一个程序来帮政府官员决定他们应拨款兴建哪些航线以使得没有出现交叉的航线最多。 
 
    
 【输入格式】  
  
  第一行一个整数N,表示分布在河两岸的城镇对数。接下来的N行每行有两个由空格分隔的正数C,D(C、D<=10^9〉,描述每一对友好城镇沿着河岸与西边境线的距离,C表示北岸城镇的距离而D表示南岸城镇的距离。在河的同一边,任何两个城镇的位置都是不同的。
 
    
 【输出格式】  
   
  在安全条件下能够开通的最大航线数目。
 
    
 【输入样例】   
   
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2


 
    
 【输出样例】  
   
4
 
    
 【数据范围】  
   
  1<=N<=500000

解题思路:根据题意,如果直接计算友好城镇在安全条件下能开通的最大航线数目,不好知道哪些航线是交叉的,所以可以先将航线按北岸城镇到边境线的距离由小到大排序,则只有排序后每条航线对应的南岸城镇到边境线的距离也是由小到大递增,才能保证航线没有交叉(即满足安全条件)。

所以该问题在排序后就变为了求最大单增子序列的问题,最简单的想法就是动态规划,设状态函数f(i)表示前i对友好城镇,以第i对友好城镇为结尾时,在安全条件下能够开通的最大航线数目,则状态转移方程为f(i)=max{f(j) | 1<=j

因为在查找满足条件的最大的f(j)时费了很多时间,如果能直接找到满足条件的最大的f(j)就好了,于是想到可以利用数组,设数组g[i]表示长度为i的上升子序列的最后一个元素的最小值,因为g[i]在计算过程中始终是单调递增(也有可能相邻的两个值相等)的(对于这一点,可以自己举例来理解) ,所以如果知道f(i)所对应的第i对友好城镇中南岸城镇到边境线的距离,就可以利用二分查找(lower_bound)找到第一个距离大于等于它第一个的上升子序列的长度,所求得的值即为f(i)的值(实际是将所求得的值先减1,再加1)。在计算f(i)的同时,要注意更新g[i]的值,如果查找时,第i对友好城镇中南岸城镇到边境线的距离是最大的或者如果此时g[f(i)]的值比a[i].d大,那么g[f(i)]=a[i].d,同时因为要使用二分查找,就要记录g数组的长度,如果查找时,第i对友好城镇中南岸城镇到边境线的距离是最大的,g数组的长度就应该加1。需要注意的是,在编写程序时,发现f(i)可以省略,在每次更新g数组后,g数组的长度就是答案。这种做法的时间复杂度为O(N*log2N)。


#include
#include
#include
#include
#include
using namespace std;
const int maxn=500005;
int N;
struct data
{
	int c,d;
};
data a[maxn];
bool cmp(data aa,data bb)
{
	return aa.ccnt || g[t]>a[i].d)  g[t]=a[i].d;  //更新g[i]
		if(t>cnt)  cnt=t;  //更新g数组长度
	}
	printf("%d\n",cnt);
}
int main()
{
	//freopen("48.in","r",stdin);
	//freopen("48.out","w",stdout);
	scanf("%d",&N);
	for(int i=1;i<=N;i++)
	scanf("%d%d",&a[i].c,&a[i].d);  //将友好城镇按北岸城镇到边境线的距离由小到大排序
	sort(a+1,a+1+N,cmp);
	solve();
	return 0;
}


你可能感兴趣的:(动态规划)