不知不觉快要暑假了,蓝桥杯不看题痛失省一,天梯赛不看题怒丢30分,下次要带眼睛比!
A - 248 G
洛谷:P3146;
合并区间自然要用区间DP;
闫氏DP法启动!
len 代表区间长度,i 代表区间起点,k 为区间的切分点;
#include <bits/stdc++.h>
#define FOR_(I, S, N) for (int I=(S), END=(N); I<=END; I++)
using namespace std;
const int C = 300;
int n, res;
int dp[C][C];
int main()
{
cin >> n;
FOR_(i, 1, n)
{
cin >> dp[i][i];
res = max(res, dp[i][i]);
}
FOR_(len, 2, n)
{
FOR_(i, 1, n - len + 1)
{
int l = i, r = i + len - 1;
FOR_(k, l, r)
if(dp[l][k] == dp[k + 1][r] && dp[l][k] > 0) dp[l][r] = max(dp[l][r], dp[l][k] + 1);
res = max(res, dp[l][r]);
}
}
cout << res;
}
时间复杂度是O(n^3);
———————————————————————————————————————————
不过这题还有更加优化的DP做法;
我们一个个来看,对于每一个数,我去看它是否能和右边相邻的数字合成,也就是看它是不是和我在同一层( j ),要是可以,我就在我这个位置上的数值k + 1的地方记录与我合成的那个数的相邻的数的位置;
对于样例来说:
我们的数组内容为:
也就是每个数右边相邻的下标就是 i + 1 ;
然后我们发现 对于第一个数,他的相邻下标的数与他在同一行,也就是他们数值相等,可以合成
便让 j + 1 记录合成后第一个数新相邻的数的下标:
意思即若我合成,则我与第3号数字相邻;
同理第一行的第二位也可以与右边合成;
但是第一行第三位就不能与右边合成了,因为他右边没有数字 (也就是0);
一行遍历一次后如下:
对于第二行,也用同样的规则处理;
1与3相邻,但3没有数字,不能合成;
2与4相邻,且4有数字,与5相邻,将新相邻数更替为5;
3没数字不考虑;
4与5相邻,但5没有数字,不能合成;
第三行中2与5相邻,但无法合成;
在这次运行中我们发现最低能下到第3层,也即 j 最大为3;
那我们就最多能合成到3;
运行且打出后内容如下:
程序:
#include <bits/stdc++.h>
#define FOR_(I, S, N) for (int I=(S), END=(N); I<=END; I++)
using namespace std;
int n, res;
int dp[250][47];
int main()
{
int x;
cin >> n;
FOR_(i, 1, n)
{
cin >> x;
dp[i][x] = i + 1;
res = max(res, x);
}
for(int j = 2; j <= res + 1; j ++)
FOR_(i, 1, n)
{
if (!dp[i][j])
dp[i][j] = dp[dp[i][j - 1]][j - 1];
if (dp[i][j])
res = max(res, j);
}
cout << res;
}
res 是有数字的层数,所以我们的循环层数就是res + 1;
时间复杂度是O(n);
B - Dynasty Puzzles
洛谷:CF191A;
构造一个字符串,要求按输入的先后,首尾一个字母相接,且最终串的首尾字母相同;
对于每个串,我们都可以选择经过其与否,只要看哪个长就OK;
#include <bits/stdc++.h>
#define FOR_(I, S, N) for (int I=(S), END=(N); I<=END; I++)
using namespace std;
const int C = 30;
int n, res;
string s;
int dp[C][C];
int main()
{
cin >> n;
FOR_(i, 1, n)
{
cin >> s;
int len = s.length(), l = s[0] - 'a', r = s[len - 1] - 'a';
FOR_(j, 0, 25)
if(dp[j][l])
dp[j][r] = max(dp[j][r], dp[j][l] + len);
dp[l][r] = max(dp[l][r], len);
}
FOR_(i, 0, 25)
res = max(res, dp[i][i]);
cout << res;
}
C - Boredom
洛谷:CF455A;
有 n 个数,每一次取一个 x ,将其删除,并将所有值为 x+1 与 x−1 的数都删除,res 加上 x ,求最后最大的 res 的值;
用贪心模拟肯定是过不了的,还是得DP;
a [ C ] 数组存储各个数字的数量,dp [ C ] 是基础的线性DP,ma 是最大的数字;
#include <bits/stdc++.h>
#define FOR_(I, S, N) for (int I=(S), END=(N); I<=END; I++)
using namespace std;
typedef long long ll;
const int C = 1e5 + 10;
int n, ma;
ll a[C], dp[C];
int main()
{
int x;
cin >> n;
FOR_(i, 1, n)
{
cin >> x;
a[x] ++;
ma = max(ma, x);
}
dp[1] = a[1];
FOR_(i, 2, ma)
dp[i] = max(dp[i - 2] + a[i] * i, dp[i - 1]);
cout << dp[ma];
}
D - Tenzing and Balls
洛谷:CF1842C;
dp [ i ] 为:到 i 这的最大删除长度;
分成两种情况:删除 a [ i ] 或不删除 a [ i ];
不删除的话 dp [ i ] = dp [ i - 1 ];
删除的话 dp [ i ] 就为 删除到前面某个最大的与 a [ i ] 相同数字的长度;
#include <bits/stdc++.h>
#define FOR_(I, S, N) for (int I=(S), END=(N); I<=END; I++)
using namespace std;
typedef long long ll;
const int C = 2e5 + 10;
int T, n;
int a[C], b[C], dp[C];
int main()
{
cin >> T;
while(T --)
{
cin >> n;
FOR_(i, 1, n) cin >> a[i];
memset(dp, 0, sizeof dp);
memset(b, -2, sizeof b);
FOR_(i, 1, n)
{
dp[i] = max(dp[i - 1], b[a[i]] + i);
b[a[i]] = max(b[a[i]], dp[i - 1] - i + 1);
}
cout << dp[n] << endl;
}
}
如上;
E - New Year and Domino
洛谷:CF611C;
其实就是子矩阵的和 (矩阵前缀和);
唯一的问题是左边第一排不计算左边,上面第一行不计算上面;
所以分两个前缀和数组;
#include <bits/stdc++.h>
#define FOR_(I, S, N) for (int I=(S), END=(N); I<=END; I++)
using namespace std;
typedef long long ll;
const int C = 510;
int n, m, k;
int a[C][C], f1[C][C], f2[C][C];
int main()
{
cin >> n >> m;
FOR_(i, 1, n)
{
getchar();
FOR_(j, 1, m)
{
char ch = getchar();
a[i][j] = (ch == '.' ? 1 : 0);
}
}
FOR_(i, 1, n)
{
FOR_(j, 1, m)
{
f1[i][j] = f1[i - 1][j] + f1[i][j - 1] - f1[i - 1][j - 1];
f2[i][j] = f2[i - 1][j] + f2[i][j - 1] - f2[i - 1][j - 1];
if(a[i][j])
{
if(a[i - 1][j]) f1[i][j] ++;
if(a[i][j - 1]) f2[i][j] ++;
}
}
}
cin >> k;
int x1, x2, y1, y2;
while(k --)
{
cin >> x1 >> y1 >> x2 >> y2;
cout << f1[x2][y2]+f1[x1][y1-1]-f1[x2][y1-1]-f1[x1][y2] + f2[x2][y2]+f2[x1-1][y1]-f2[x2][y1]-f2[x1-1][y2] << endl;
}
}
之后还要学 Python 和 Java;
大脑要开始旋转了!