2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛)

完型填空

思路:背包

  1. 用dp[i][j[k][h]表示在选了i道题的情况下,其中选了j道a,k道b,h道c,i-(j+k+h)道d时的最大期望
  2. 注意是每层循环,每道题选的数目不超过n/4,在选第i道题目时,之前选的所有题目和应该小于i
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
const int N = 110;
int dp[N][30][30][30];
int a[N][5];
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)cin >> a[i][1] >> a[i][2] >> a[i][3] >> a[i][4];
	for (int i = 1; i <= n; ++i)for (int j = 0; j <= n / 4 && j < i; ++j)for (int k = 0; k <= n / 4 && k + j < i; ++k)for (int h = 0; h <= n / 4 && j + k + h < i; ++h)
					{
						int tmp = dp[i - 1][j][k][h];
						dp[i][j][k][h] = max(dp[i][j][k][h], tmp + a[i][4]);
						dp[i][j + 1][k][h] = max(dp[i][j + 1][k][h], tmp + a[i][1]);
						dp[i][j][k + 1][h] = max(dp[i][j][k + 1][h], tmp + a[i][2]);
						dp[i][j][k][h + 1] = max(dp[i][j][k][h + 1], tmp + a[i][3]);
					}
	cout << dp[n][n / 4][n / 4][n / 4] << endl;
	return 0;
}

摘苹果

思路:线段树裸题线段树

  1. 注意优化是显然我们每次需要具体到每个点去除3,但是我们如果[l,r]的区间没有一个数大于10,我们还需要进去这个区间吗?
  2. 所以每个区间维护一个区间最大值maxn,如果区间最大值都小于10,这个区间不用更新
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
const int N = 1e5 + 10;
int a[N];
struct tree
{
	int l, r;
	ll sum, cnt, maxn;
} t[N << 2];

void build(int l, int r, int p)
{
	t[p].l = l, t[p].r = r, t[p].cnt = 0;
	if (l == r)
		{
			t[p].sum = t[p].maxn = a[l];
			if (t[p].sum < 100)t[p].cnt = 1;
			return;
		}
	int mid = l + ((r - l) >> 1);
	build(l, mid, p << 1);
	build(mid + 1, r, p << 1 | 1);
	t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;
	t[p].cnt = t[p << 1].cnt + t[p << 1 | 1].cnt;
	t[p].maxn = max(t[p << 1].maxn, t[p << 1 | 1].maxn);
}

void update(int l, int r, int p)
{
	if (t[p].maxn < 10)return;
	if (t[p].l == t[p].r)
		{

			t[p].sum = t[p].maxn = t[p].sum * 2 / 3;
			if (t[p].sum < 100)t[p].cnt = 1;
			return;
		}
	int mid = t[p].l + ((t[p].r - t[p].l) >> 1);
	if (l <= mid)update(l, r, p << 1);
	if (r > mid)update(l, r, p << 1 | 1);
	t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;
	t[p].cnt = t[p << 1].cnt + t[p << 1 | 1].cnt;
	t[p].maxn = max(t[p << 1].maxn, t[p << 1 | 1].maxn);
}

ll asksum(int l, int r, int p)
{
	if (t[p].r <= r && l <= t[p].l)return t[p].sum;
	int mid = t[p].l + ((t[p].r - t[p].l) >> 1);
	ll ans = 0;
	if (l <= mid)ans += asksum(l, r, p << 1);
	if (r > mid)ans += asksum(l, r, p << 1 | 1);
	return ans;
}
ll askcnt(int l, int r, int p)
{
	if (t[p].r <= r && l <= t[p].l)return t[p].cnt;
	int mid = t[p].l + ((t[p].r - t[p].l) >> 1);
	ll ans = 0;
	if (l <= mid)ans += askcnt(l, r, p << 1);
	if (r > mid)ans += askcnt(l, r, p << 1 | 1);
	return ans;
}
int main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)cin >> a[i];
	int op, x, y;
	build(1, n, 1);
	while (m--)
		{
			cin >> op >> x >> y;
			if (op == 1)update(x, y, 1);
			else if (op == 2)cout << askcnt(x, y, 1) << endl;
			else if (op == 3)cout << asksum(x, y, 1) << endl;
		}

	return 0;
}

前缀复制机

思路:

名字暗示的很明显了,就是前缀函数裸题前缀函数与KMP算法

  1. 我们处理好字符串的前缀和后,从后往前遍历。这样贪心即是最优解
  2. 不从前是因为,后面过去,能匹配多大的前缀已经求出来了,而如果是从前往后,假设你这里可以先匹配3个,后面假设没得匹配。但是,假设如果你这里先匹配2个,后面就可以全部匹配完。那是不是后面这种方案更好。
  3. 所以从后往前推是知道了前面的所有情况做出的最优解(每次尽可能减少后面的长度)
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
typedef pair<int, int> pii;

//double 型memset最大127,最小128
//std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 1e5 + 10;
int p[N];
int main()
{
	int t;
	cin >> t;
	while (t--)
		{
			int n;
			string s;
			cin >> n >> s;
			p[0] = 0;
			for (int i = 1; i < (int)s.size(); ++i)
				{
					int j = p[i - 1];
					while (s[j] != s[i] && j > 0)j = p[j - 1];
					if (s[j] == s[i])j++;
					p[i] = j;
				}
			//从0开始,所有num最后下标是n-1
			int num = n - 1;
			int ans = 0;
			while (num >= 0)
				{
					ans++;
					if (!p[num])num--;
					else
						{
							int tmp = p[num];
							while (tmp * 2 > num + 1 && tmp > 0)tmp = p[tmp - 1];
							num -= tmp;
						}
				}
			cout << ans << endl;

		}
	map<int, int *>mp;

	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值