有趣的序列
最长上升子序列(LIS)
南昌理工学院ps:(这周划水严重,就不发学习总结了)
最长上升子序列LIS)
LIS(最长上升子序列)(本蒟蒻只会用) 想理解点这
n*n模板
//定义a[n]为要求的序列
for (int i = 1; i <= n; ++i) {
na = 0;
for (int j = 1; j <= i; ++j) {//寻找到a[i]的最长子序列长度
if (a[i] > a[j])
na = max(na, dp[j]);
}
dp[i] = na + 1;
}
nlogn模板
用二分的方法进行查找比较,节省时间
//查找a[n],
int len = 1;
memset(d, 127, sizeof(d));
d[len] = a[1];
for (int i = 2; i <= n; ++i){
if (d[len] >= a[i]) d[++len] = a[i];
else {
int l = 0, r = len;
while (l < r) {
int mid = (l + r) >> 1;
if (d[mid] >= a[i]) l = mid + 1;
else r = mid;
}
d[l] = a[i];
}
}
cout << len << endl;
LIS(n *n)经典例题合唱队形
解法:先正着求一遍LIS再反求一遍LIS,取最大值 - 1即可(因为最大值算了两遍)。
举例:
输入 8
186 186 150 200 160 130 197 220
正求LIS 1 1 1 2 1 1 2 3
反求LIS 2 2 1 3 2 1 1 1
max 3 3 2 5 3 2 3 4
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int n, ans = 0, na;
int dp[101], f[101];
int a[101];
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i <= n; ++i) {
na = 0;
for (int j = 1; j <= i; ++j) {
if (a[i] > a[j])
na = max(na, dp[j]);
}
dp[i] = na + 1;
}
for (int i = n; i > 0; --i) {
na = 0;
for (int j = n; j >= i; --j) {
if (a[i] > a[j])
na = max(na, f[j]);
}
f[i] = na + 1;
}
for (int i = 1; i <= n; ++i)
{
ans = max(ans, f[i] + dp[i]);
}
cout << n - ans + 1 << endl;
return 0;
}
LIS(nlogn)例题 导弹拦截
解法:
第一问正着用LIS(nlogn)求一遍最长下降子序列。
第二问正着用LIS(nlogn)求一遍最长上升子序列的长度(因为有一个递增的话,就要多一台。)不理解的可以手推或者去看看狄尔沃斯定理 (看了可能会… )。
ps:因为数据过大,用 n* n 的求LIS只能过一半(100分),所以要用nlogn(200分)。
AC代码
#include<iostream>
#include<cstring>
using namespace std;
int a[1000001], d[100001];
int u, n = 0, ans = 0, len = 1;
int main()
{
while (cin >> u)
{
a[++n] = u;
}
memset(d, 127, sizeof(d));
d[len] = a[1];
for (int i = 2; i <= n; ++i){
if (d[len] >= a[i]) d[++len] = a[i];
else {
int l = 0, r = len;
while (l < r) {
int mid = (l + r) >> 1;
if (d[mid] >= a[i]) l = mid + 1;
else r = mid;
}
d[l] = a[i];
}
}
cout << len << endl;
len = 1;
memset(d, -1, sizeof(d));
d[1] = a[1];
for (int i = 2; i <= n; ++i) {
if (d[len] < a[i]) d[++len] = a[i];
else {
int l = 0, r = len;
while (l < r) {
int mid = (l + r) >> 1;
if (d[mid] >= a[i]) r = mid;
else l = mid + 1;
}
d[l] = a[i];
}
}
cout << len << endl;
return 0;
}
LCS(最长公共子序列)
解释
n*n模板
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000;
char a[N],b[N];
int dp[N][N];
int main()
{
int lena,lenb,i,j;
while(scanf("%s%s",a,b)!=EOF)
{
memset(dp,0,sizeof(dp));
lena=strlen(a);
lenb=strlen(b);
for(i=1;i<=lena;i++)
{
for(j=1;j<=lenb;j++)
{
if(a[i-1]==b[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
printf("%d\n",dp[lena][lenb]);
}
return 0;
}
nlogn
- 转化:将LCS问题转化成LIS问题。
假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。
记录s1中每个元素在s2中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:
loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。
将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。
在对s3求LIS得到的值即为求LCS的答案。(出处
例题回文子串
思路:把得到的序列a倒序处理一下,得到一个新序列b.
求一下a, b的最长公共子序列的长度n,输出a序列长度 - n.
AC代码
#include<iostream>
#include<string>
using namespace std;
string a, b;
int dp[1001][1001];
int len1, len2;
void lcs(int i,int j)
{
for(i=1; i<=len1; i++)
{
for(j=1; j<=len2; j++)
{
if(a[i-1] == b[j-1])
dp[i][j] = dp[i-1][j-1] + 1;
else if(dp[i-1][j] > dp[i][j-1])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = dp[i][j-1];
}
}
}
int main()
{
cin >> a;
len1 = len2 = a.size();
for (int i = len1 - 1; i >= 0; --i)
{
b += a[i];
}
lcs(len1, len2);
cout << len1 - dp[len1][len2];
return 0;
}
nlogn 例题模板题
AC代码
#include<iostream>
#include<cstdio>
using namespace std;
int a[100001], b[100001], map[100001], f[100001];
int n, len = 0;
int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i) a[i] = read(), map[a[i]] = i;
for (int i = 1; i <= n; ++i) b[i] = read(), f[i] = 0x7fffffff;
for (int i = 1; i <= n; ++i) {
if (map[b[i]] > f[len]) f[++len] = map[b[i]];
else {
int l = 0, r = len, mid;
while (l < r) {
mid = (l + r) / 2;
if (f[mid] > map[b[i]]) r = mid;
else l = mid + 1;
}f[l] = min(map[b[i]], f[l]);
}
}
cout << len;
return 0;
}