最近准备开始重拾DP,此题本质是求最长上升子序列,用a[u]=v表示当前第u个贫穷的城市对应的对面第v个富裕的城市,然后dp[i]表示当前长度为i时的最小a[k]值,为什么是最小呢?因为只有这样才能保证上升序列达到最长长度,只有尽可能的将dp[len]的值变小才能加入更多的a[i],从而延长数列长度,需要注意最后输出如果只有一条路的话road是没有s的,等到全部扫描完了之后直接打印出最长的长度就可以了
#include <iostream>
using namespace std;
const int size = 500000;
int a[size+10];
int dp[size+10];
int search(int x, int left, int right, int *d)//这里使用二分搜索更为优化,下面附有普通搜索也可以过,查找第一个大于等于x的数,然后用x覆盖掉
{
while (left <= right){
int mid = (left+right)>> 1;
if (x >= d[mid])left = mid+1;
else right = mid-1;
}
return left;
}
/*int search(int x, int *d, int len)
{
for (int i = 1; i <= len; i ++){
if (d[i] >= x)return i;
}
}*/
int main()
{
int n;
int nc = 0;
while (scanf("%d", &n) != EOF){
for (int i = 1; i <= n; i ++){
int u, v;
scanf("%d%d", &u, &v);
a[u] = v;
}
int ans = 1;
dp[1] = a[1];
for (int i = 2; i <= n; i ++){
if (a[i] > dp[ans]){
ans ++;
dp[ans] = a[i];
}
else {//search(a[i], dp, ans)
dp[search(a[i], 1, ans, dp)] = a[i];
}
}
//这里有's'的问题要注意下
if (ans <= 1)
printf("Case %d:\nMy king, at most %d road can be built.\n\n", ++nc, ans);
else
printf("Case %d:\nMy king, at most %d roads can be built.\n\n", ++nc, ans);
}
return 0;
}