pezhzh的快乐寒假作业[2024/1/21]

不知不觉快要暑假了,蓝桥杯不看题痛失省一,天梯赛不看题怒丢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;

大脑要开始旋转了!

  • 14
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值