一.最长上升(下降)子序列

https://www.luogu.org/problemnew/show/P1091

https://www.luogu.org/problemnew/show/P1020

P1091:

让每一个人做最中间人,然后分别求到俩边的最长下降子序列

注意要选取中间人为最高的对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*-------------------------------------------------------*/

int a[MAX];
int dp[MAX];

/* --------------------------------------------------------------*/



int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
/* --------------------------------------------------------------*/
cin >>N;
for(int i = 1;i <= N;i++)
{
cin >> a[i];
}
int mi = INF;
for(int k = 1;k <= N;k++)
{
fill(dp,dp+MAX,0);
int ans =0;
dp[0] = a[k];
for(int i = k-1;i >= 1;i--)
{
for(int j = k-1;j >= 1;j--)
{
if(dp[j-1] > a[i])
{
dp[j] = max(a[i],dp[j]);
}
if(dp[j] != 0 && j > ans) ans = j;
}
}
int x = k - 1 - ans;

fill(dp,dp+MAX,0);
dp[0] = a[k];
ans = 0;
for(int i = k+1;i <= N;i++)
{
for(int j = N-k;j >= 1;j--)
{
if(dp[j-1] > a[i])
{
dp[j] = max(a[i],dp[j]);
}
if(dp[j] != 0 && j > ans) ans = j;
}
}
int y = N - k - ans;

if(mi > x + y)
{
mi = x + y;
}
}
cout << mi;
return 0;
}

P1020

状态转移方程和上面一样

dp[i]代表的是截拦i个火箭的时候的最高高度,不能截拦的时候为0

关键语句:

1
2
3
4
if(dp[j-1] >= a[i])
{
dp[j] = max(a[i],dp[j]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*-------------------------------------------------------*/

int a[MAX];
int dp[MAX];
vector<int> b;
/* --------------------------------------------------------------*/



int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

N= 1;
int x;
int cnt = 0;
while(cin >> x)
{
a[N++] = x;
if(b.size() == 0)
{
b.push_back(x);
cnt++;
}
else
{
int i= 0;
for(i = 0;i < cnt;i++)
{
if(b[i] >= x)
{
b[i] = x;
break;
}
}
if(i == cnt)
{
b.push_back(x);
cnt++;
}
}
sort(b.begin(),b.end());
}
N--;

dp[0] = INF;
for(int i = 1;i <= N;i++)
{
if(dp[i-1] >= a[i])
{
dp[i] = a[i];
}
for(int j = i-1;j >= 1 ;j--)
{
if(dp[j-1] >= a[i])
{
dp[j] = max(a[i],dp[j]);
}
}
}

for(int i = N;i >= 1;i--)
{
if(dp[i] != 0)
{
cout << i <<"\n";
break;
}
}
cout << cnt ;
return 0;
}

二.区间dp

https://www.luogu.org/problemnew/show/P1880

p1880:

首先这个题是环,我们要把环化为链

1
2
3
4
5
for(int i = 1;i <= N;i++)
{
cin >> a[i];
a[i+N] = a[i];
}

还有做一下前缀和的处理,前缀和就是前面的数组元素的和

1
2
3
4
for(int i = 1;i <= 2*N;i++)
{
sum[i] = sum[i-1] + a[i];
}

dp[i][j] 代表的是区间(i,j)的最大和or小

首先我们枚举的是区间的长度,而不是(i,j)

一旦区间长度l确定了,枚举i,j是固定的

然后通过k,把俩堆堆到一起

关键代码:dpa[i][k] + dpa[k+1][j] 注意后面是k+1不是k

1
2
dpa[i][j] = max(dpa[i][j],dpa[i][k] + dpa[k+1][j] + sum[j] - sum[i-1]);
dpi[i][j] = min(dpi[i][j],dpi[i][k] + dpi[k+1][j] + sum[j] - sum[i-1]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*-------------------------------------------------------*/

int a[2*MAX];
int dpa[2*MAX][2*MAX];
int dpi[2*MAX][2*MAX];
int sum[2*MAX];

/* --------------------------------------------------------------*/



int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
/* --------------------------------------------------------------*/
cin >> N;
for(int i = 1;i <= N;i++)
{
cin >> a[i];
a[i+N] = a[i];
}
sum[0] = 0;
for(int i = 0;i <= N;i++)
{
fill(dpa[i],dpa[i]+N+1,0);
fill(dpi[i],dpi[i]+N+1,0);
}
for(int i = 1;i <= 2*N;i++)
{
sum[i] = sum[i-1] + a[i];
}
for(int l = 2;l <= N;l++)
{
for(int i = 1;i+l-1 <= 2*N;i++)
{
int j = i + l - 1;
dpi[i][j] = INF;//why?why?why?
for(int k = i;k < j;k++)
{
dpa[i][j] = max(dpa[i][j],dpa[i][k] + dpa[k+1][j] + sum[j] - sum[i-1]);
dpi[i][j] = min(dpi[i][j],dpi[i][k] + dpi[k+1][j] + sum[j] - sum[i-1]);

//cout << i << " " << j << " " << k <<endl;
//cout <<dpa[i][j] << " " << dpi[i][j] <<endl;
}
}
}
int ma = 0;
int mi = INF;
for(int i = 1;i <= N;i++)
{
ma = max(ma,dpa[i][i+N-1]);
mi = min(mi,dpi[i][i+N-1]);
}
cout << mi << "\n" << ma << "\n";
return 0;
}