题意:n*m的网格,有些格子里可能有宝藏,若站在(x,y),每次只能向(x+1,y)或者(x,y+1)方向走,问最少要走几趟能将网格中的宝物收集完。
分析: 按题目的意思就是只能向右和向下走。如果将宝物的坐标按x轴来从小到大排(x轴相同的按y轴从小到大),那么就可以只考虑y坐标了
,假设有两个位置i,j,i.x<=j.x,当i.y<=j.y时,才有可能收集完i后收集j,所以按x轴排序完后,问题就转化为用最少几个不降子序列可将y轴坐标序列全部涵盖。那就是最长(严格)递减子序列的长度了。
#include <iostream>
#include <stdio.h>
#include <memory.h>
#include <algorithm>
using namespace std;
struct node
{
int x,y;
}a[100010];
int dp[100010],num[100010],b[100010],n,m,p;
int cmp(node p,node q)
{
if(p.x==q.x) return p.y<q.y;
return p.x<q.x;
}
void solve() // 求最长递减子序列
{
int i,tem,l,r,mid,ans,top;
b[1]=num[1]; top=1; dp[1]=1;
ans=0;
for(i=2;i<=p;i++)
{
if(num[i]<b[top])
{
b[++top]=num[i];
dp[i]=top;
}
else
{
l=1; r=top;
while(l<=r)
{
mid=(l+r)>>1;
if(b[mid]<=num[i])
{
tem=mid;
r=mid-1;
}
else l=mid+1;
}
b[tem]=num[i];
dp[i]=tem;
}
if(dp[i]>ans) ans=dp[i];
}
printf("%d\n",ans);
}
int main()
{
int i;
while(scanf("%d%d%d",&m,&n,&p)!=EOF)
{
for(i=1;i<=p;i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+1+p,cmp);
for(i=1;i<=p;i++)
num[i]=a[i].y;
solve();
}
return 0;
}