HDU 1025 Constructing Roads In JGShining's Kingdom(构建道路:LIS问题)
http://acm.hdu.edu.cn/showproblem.php?pid=1025
题意:
有2n个点分布在平行的两条直线上, 上面那条是富有城市的1到n个点(从左到右分布), 下面那条是贫穷城市1到n个点(从左到右分布). 现在给出每个贫穷城市需要连接的富有城市的编号, 即(i,j)表示i贫穷城市只能连接j号富有城市 , 问你最多能构建几条贫穷城市到富有城市间的连线, 使得任意两条连线不相交(就算端点相交也不行,即不同的贫穷城市不能连接到同一个富有城市)?
分析:
假设我们选择了第i号贫穷城市和第j号贫穷城市 (且i<j) 来连接它们对应的富有城市, 只有i连接的富有城市编号<j连接的富有城市编号才不会出现相交的情况. (想想是不是)
所以现在的问题是: 我们要对贫穷城市按1到n编号的城市选出一个最长上升子序列(序列编号是对应的富有城市的编号)即可.
由于n的规模达到50W, 所以只能用O(nlogn)的算法求.
令g[i]==x表示当前遍历到的长度为i的所有最长上升子序列中的最小序列末尾值为x.(如果到目前为止, 根本不存在长i的上升序列, 那么x==INF无穷大)
假设当前遍历到了第j个值即a[j], 那么先找到g[n]数组的值a[j]的下确界(即第一个>=a[j]值的g[k]的k值). 那么此时表明存在长度为k-1的最长上升子序列且该序列末尾的位置<j且该序列末尾值<a[j].
那么我们可以令g[k]=a[j] 且 dp[i]=k (dp含义如解法1).
(上面一段花时间仔细理解)
最终我们可以找出下标最大的i使得: g[i]<INF 中i下标最大. 这个i就是LIS的长.
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1e8
using namespace std;
const int maxn=500000+5;
int n;
int a[maxn];
int g[maxn];
int main()
{
int kase=0;
while(scanf("%d",&n)==1)
{
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x]=y;
}
for(int i=1;i<=n;i++)
g[i]=INF;
int ans=0;
for(int i=1;i<=n;i++)
{
int k=lower_bound(g+1,g+n+1,a[i])-g;
g[k]=a[i];
ans=max(ans,k);
}
printf("Case %d:\nMy king, at most %d road%s can be built.\n\n",++kase,ans,ans==1?"":"s");
}
return 0;
}