最长递增子序列(LIS)问题。。。
一开始我没有想到是LIS,但是知道要用dp做
我的想法是用dp[i][j]表示上面一行从第1个到第i个城市与下面一行从第1个到第j个城市之间所能修的最多路
用match[i][j]=true表示上面一行的城市i与下面一行的城市j可以修路
很容易得到状态方程:dp[i][j]=max{ dp[i-1][j], dp[i][j-1] }+match[i][j]
但题目中n的最大值是500,000因此用二维数组会超内存的
改用一维数组dp[i]表示以a[i]为最后一个元素的LIS个数,那么所有dp的最大值即为所求。
要求出所有dp,我的想法是外围循环从1到n,内层循环查找当前a[i]对应的dp[i],时间复杂度是O(n*n),所以超时
最后听炳昌大神的话看了白书上的说明,还有一个O(n*logn)的算法
对所有dp值为i的序列,取其最先结束的一个可以保证结果最优
优化的就是dp[i]的求解方法,自己看的也是一知半解,慢慢领会吧
代码如下:
#include <map>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define esp 1e-9
#define MAXN 500010
#define ll long long
#define INF 0x7FFFFFFF
#define BUG system("pause")
#define SW(a,b) a^=b;b^=a;a^=b;
using namespace std;
int dp[MAXN];
int a[MAXN];
int g[MAXN];
int main(void){
int T = 0;
int n;
freopen("out.txt", "w", stdout);
while(scanf("%d", &n) != EOF){
T++;
int poor, rich;
for(int i=1; i<=n; ++i){
scanf("%d %d", &poor, &rich);
a[poor] = rich;
}
memset(dp, 0, sizeof(dp));
for(int i=1; i<=n; ++i){
g[i] = INF;
}
int maxdp = 0;
for(int i=1; i<=n; ++i){
int k = lower_bound(g+1, g+n+1, a[i]) - g;//以 a[i] 为终点时对应的dp值
cout << "k = " << k << endl;
dp[i] = k;
cout << "dp[" << i << "] = " << dp[i] << endl;
g[k] = a[i];
for(int j=1; j<=n; ++j) {
if(g[j] == INF)
break;
cout << "g[" << j << "] = " << g[j] << endl;
}
maxdp = max(maxdp, dp[i]);
}
printf("Case %d:\n", T);
if(maxdp == 1)
printf("My king, at most 1 road can be built.\n\n");
else
printf("My king, at most %d roads can be built.\n\n", maxdp);
}
return 0;
}