NC16664 [NOIP2004]合唱队形
题目链接
关键点:
1、题目要求求出连续的最长上升子序列+最长下降子序列
2、我们求出从左到右的每个点最长上升子序列,从右到左每个点的最长上升子序列
3、答案即为看每个位置的最长上升子序列长度+最长下降子序列长度的最大值
完整代码:
# include<bits/stdc++.h>
using namespace std;
int n, ans;
int a[110];
int dpr[110], dpl[110];
int main()
{
cin>>n;
for (int i=1; i<=n; i++)
{
cin>>a[i];
dpr[i] = 1;
dpl[i] = 1;
}
for (int i=1; i<=n; i++)
{
for (int j=1; j<i; j++)
{
if (a[i]>a[j])
dpl[i] = max(dpl[i], dpl[j]+1);
}
}
for (int i=n; i>=1; i--)
{
for (int j=n; j>i; j--)
{
if (a[i]>a[j])
dpr[i] = max(dpr[i], dpr[j]+1);
}
}
for (int i=1; i<=n; i++)
{
// cout<<dpl[i]<<" "<<dpr[i]<<endl;
ans = max(ans, dpl[i]+dpr[i]-1);
}
cout<<n-ans<<endl;
return 0;
}
NC235954 滑雪
题目链接
关键点:
1、对于每一个点的下滑长度,为其四周下滑长度+1的值取最大值
2、最好采用记忆化搜索,如果采用直接递推,不能按照数组的顺序去推,一个数的四周不一定在之前已经算出来。采用记忆化搜索,遇到没算出来的值,可以先调用函数先算出
完整代码:
# include <bits/stdc++.h>
using namespace std;
int n, m;
int g[110][110];
int dp[110][110];
int X[5] = {1, -1, 0, 0};
int Y[5] = {0, 0, 1, -1};
int find(int x, int y)
{
if (dp[x][y])
return dp[x][y];
dp[x][y] = 1;
for (int i=0; i<4; i++)
{
int x1 = X[i] + x;
int y1 = Y[i] + y;
if (x1<=0||y1<=0||x1>n||y1>m)
continue;
if (g[x][y] > g[x1][y1])
dp[x][y] = max(dp[x][y], find(x1, y1)+1);
}
return dp[x][y];
}
int main()
{
cin>>n>>m;
for (int i=1; i<=n; i++)
{
for (int j=1; j<=m; j++)
cin>>g[i][j];
}
int ans = 0;
for (int i=1; i<=n; i++)
{
for (int j=1; j<=m; j++)
{
// cout<<i<<" "<<j<<" "<<find(i, j)<<endl;
ans = max(ans, find(i, j));
}
}
cout<<ans<<endl;
return 0;
}
NC235948 最大子串和
题目链接
关键点:
1、对于一个数组,对于每一位,我们都算出选择该数的最大值,那么就有就和是否选择之前的数进行比较
dp[i] = max(dp[i-1]+a[i], a[i]);表示选择当前数的最大值(一定选择当前数,看是否选择之前的数)
完整代码:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll a[1000000+10];
ll dp[1000000+10];
ll ans;
int main()
{
cin>>n;
for (int i=1; i<=n; i++)
cin>>a[i];
for (int i=1; i<=n; i++)
{
dp[i] = max(a[i]+dp[i-1], a[i]);
}
for (int i=1; i<=n; i++)
{
ans = max(ans, dp[i]);
}
cout<<ans<<endl;
return 0;
}
NC235624 牛可乐和最长公共子序列
题目链接
关键点:
1、注意该题有多组数据!
2、对于两个序列,我们取两个序列的结尾来推
3、dp[i][j];表示第一个序列到i,但不包括i,第二个序列到j ,但不包括j
那么比较i前一个字母和j的前一个字母看是否相同,相同则为之前的+1,即
dp[i][j] = dp[i-1][j-1]+1;
该式子也表明了我们为什么不将动态转移方程设为包括i, j,如果从0开始遍历,数组下标会越界
4、如果i-1字母和j-1不相同,那么就在两个串分别减少一个长度中取最大值
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
5、因为该方程是从头一点点遍历到尾,所以最终答案即为dp[s1.size()][s2.size()];
完整代码:
# include <bits/stdc++.h>
using namespace std;
string s1, s2;
int dp[5010][5010];
int main()
{
while(cin>>s1>>s2)
{
// memset(dp, 0, sizeof(dp));
for (int i=1; i<=s1.size(); i++)
{
for (int j=1; j<=s2.size(); j++)
{
if (s1[i-1] == s2[j-1])
dp[i][j] = dp[i-1][j-1]+1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
cout<<dp[s1.size()][s2.size()]<<endl;
}
return 0;
}