线性DP
线性DP(部分)
例:aw272 LCIS
272. 最长公共上升子序列 - AcWing题库
思路分析:
题目是LCS与LIS的结合,那么我们显然可以结合两道经典例题的思路, 定义f[i] [j] 表示,在a[1...i] 与 b[1...j] 中出现的以B[j]为结尾的数字。
随后我们先遍历a[i],再遍历b[j], 接下来分两种情况讨论,
①如果a[i] != b[j], 那么显然此时f[i] [j] = f[i-1] [j], 就以b[j]为结尾的情况和a[i-1]时一样。
②如果a[i] == b[j],那么此时我们是以b[j]为结尾,我们应该从一个以小于b[j]的b[k]为结尾的最大子串转移而来,因此遍历f[i-1] [k] ,\(1\leq k\leq j-1,且b[k],取出最大的即可。
优化:
时间复杂度优化: 在寻找f[i-1] [k] 时显然我们重复查找了很多遍,并且我们发现在查找时,i是不变的, 那么对于每一层i来说,我们可以定义变量maxv,用maxv来保存1-j的最大f[i-1] [k],如果b[j] < a[i] (需要用到maxv去更新的时候a[i] == b[j]),那么我们就更新maxv的值为max(maxv, f[i-1] [j])
代码示例:
int a[3005];
int b[3005];
int n;
int dp[3010][3010];
int main ()
{
cin >> n;
For(i, 1, n) cin >> a[i];
For(i, 1, n) cin >> b[i];
For(i, 1, n)
{
int maxv = 0;
For(j, 1, n)
{
if(a[i] != b[j]) dp[i][j] = dp[i-1][j];
else dp[i][j] = maxv + 1;
if(b[j] < a[i]) maxv = max(maxv, dp[i][j]);
}
}
int ans = 0;
For(i, 1, n)
ans = max(ans, dp[n][i]);
cout << ans << endl;
return 0;
}
空间复杂度优化:由于我们每一层的dp[i] [j]只与上一层dp[i-1]有关,因此我们可以把第一维去掉
代码示例:
int a[3005];
int b[3005];
int n;
int dp[3010];
int main ()
{
cin >> n;
For(i, 1, n) cin >> a[i];
For(i, 1, n) cin >> b[i];
For(i, 1, n)
{
int maxv = 0;
For(j, 1, n)
{
if(a[i] == b[j]) dp[j] = maxv + 1;
if(b[j] < a[i]) maxv = max(maxv, dp[j]);
}
}
int ans = 0;
For(i, 1, n)
ans = max(ans, dp[i]);
cout << ans << endl;
return 0;
}
例:C - Monkey and Banana
思路分析:
由于我们要求最大高度,可以利用贪心的思想,从下往上让底的长和宽尽可能的大,由题意可知, 一组xyz,可以生成三种积木,我们将所有种类的积木放进数组并进行排序,排序条件是长和宽要尽可能的大。随后题目变成了求最长上升子序列的题目,套模板求解即可
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
struct node
{
int x, y, z;
bool operator < (const node &t) const
{
if(x == t.x) return y < t.y;
return x < t.x;
}
};
vector v;
void add(int x, int y, int z)
{
node t;
t.x = x, t.y = y, t.z = z;
v.push_back(t);
}
int f[10000];
int main ()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
int cnt = 0;
while(cin >> n, n)
{
cnt++;
clr(f);
v.clear();
int x, y, z;
For(i, 1, n)
{
cin >> x >> y >> z;
add(x, y, z);
add(x, z, y);
add(y, z, x);
add(y, x, z);
add(z, x, y);
add(z, y, x);
}
sort(v.begin(), v.end());
int k = v.size();
for(int i = 0; i < k; i ++)
{
f[i] = v[i].z;
for(int j = 0; j <= i - 1; j ++)
{
if(v[j].x < v[i].x && v[j].y < v[i].y)
f[i] = max(f[i], f[j] + v[i].z);
}
}
int ans = 0;
For(i, 0, k-1) ans = max(ans, f[i]);
printf("Case %d: maximum height = %d\n", cnt, ans);
}
return 0;
}
LIS 记录路径
例:kuangbin带你飞]专题十二 基础DP1 - Virtual Judge (ppsucxtt.cn)
LIS方法:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
struct node
{
int w, s, id;
bool operator < (const node &x) const
{
if(s == x.s) return w < x.w;
return s > x.s;
}
}mouse[1005];
int f[1005];
int pos[1005];
int listt[1005];
int main ()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n = 1;
int ww, ss;
while(scanf("%d%d", &ww, &ss) != EOF)
{
mouse[n].w = ww, mouse[n].s = ss;
mouse[n].id = n;
n ++;
}
n--;
sort(mouse + 1, mouse + 1 + n);
int len = 0;
For(i, 1, n)
{
f[i] = 1;
For(j, 1, i - 1)
{
if(mouse[j].s > mouse[i].s && mouse[j].w < mouse[i].w)
f[i] = max(f[i], f[j] + 1);
}
len = max(len, f[i]);
}
cout << len << endl;
int anslen = len;
int tag = 0;
Fod(i, n, 1)
{
if(f[i] == len)
{
listt[++tag] = mouse[i].id;
len --;
}
if(len == 0) break;
}
Fod(i, anslen, 1) cout << listt[i] << endl;
return 0;
}
/*
*/
思路分析:先排个序,然后转化成求LIS or LDS。求LIS或者LDS不难,这题重点在于记录路径
方法:求完LIS后逆序遍历dp数组,比如最长长度为5,我们只需逆序遍历找出第一个为5的dp[i], i即为所求的第五个数字,接下来找出第一个为4的dp[i]....以此类推
这题求LDS方便点,就不用逆序遍历了,最后输出的时候可以直接按照从1到len的顺序输出,因为此时是从左往右正序依次找出长度为len-i的dp[i],找到直接输出即可。
LDS方法:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
struct node
{
int w, s, id;
bool operator < (const node &x) const
{
if(w == x.w) return s > x.s;
return w < x.w;
}
}mouse[1005];
int f[1005];
int pos[1005];
int listt[1005];
int main ()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n = 1;
int ww, ss;
while(scanf("%d%d", &ww, &ss) != EOF)
{
mouse[n].w = ww, mouse[n].s = ss;
mouse[n].id = n;
n ++;
}
n--;
sort(mouse + 1, mouse + 1 + n);
int len = 0;
Fod(i, n, 1)
{
f[i] = 1;
For(j, i, n)
{
if(mouse[j].s < mouse[i].s && mouse[j].w > mouse[i].w)
f[i] = max(f[i], f[j] + 1);
}
len = max(len, f[i]);
}
cout << len << endl;
For(i, 1, n)
{
if(f[i] == len)
{
cout << mouse[i].id << endl;
len--;
}
if(len == 0) break;
}
cout << endl;
return 0;
}
/*
*/