刷题历程,不断进步
前言
题目来源:第一届ACC(AcWing Cup)全国高校联赛(初赛)
题目数量:3
题目难度:递进式,易中难,一道模拟,一道枚举,一道dp
第一题模拟
原名:数圈圈
思路:
这是一道典型的模拟类型题
思路点是:短除法
起初的思路是每次输入后用 if
语句判断
后来更优的做法是开一个数组,记录这16个数对应的值
#include <bits/stdc++.h>
using namespace std;
int cnt[16] = {//记录对应数值
1, 0, 0, 0,
1, 0, 1, 0,
2, 1, 1, 2,
0, 1, 0, 0};
int main()
{
int n;
cin >> n;
if (!n)//特判一下第一个数
cout << 1 << endl;
else
{
int res = 0;
while (n)//短除法
{
res += cnt[n % 16];
n /= 16;
}
cout << res << endl;
}
return 0;
}
反思:没有想到要用数组,而是手写了判断条件
附上初阶代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
int a[N];
int main()
{
int x,q;
cin >> x;
int res = 0;
int i = 0;
q = x;
while (x > 0)
{
if (x > 16)
a[i] = x % 16;
else
a[i] = x;
i++;
x = x / 16;
}
for (int j = 0; j < i; j++)
{
if (a[j] == 0 || a[j] == 4 || a[j] == 6 || a[j] == 9 || a[j] == 10 || a[j] == 13)
res++;
else if (a[j] == 8 || a[j] == 11)
res += 2;
else
continue;
}
if (q == 0)
cout << 1 << endl;
else
cout << res << endl;
return 0;
}
第二天枚举
原名:农田灌溉
这题的做法有很多,这里只是其中一种,枚举的方法。二分也能做,感兴趣的可以自己研究研究
题目意思:找出洒水器到农田的最长距离
做法:记录每两个洒水器到中间农田的时间,找出最久的时间
用一个 max()
函数记录俩俩洒水器之间的农田到洒水器的距离
保持更新max()
函数
特判一下首尾的洒水器
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 210;
int p[N];//洒水器
int n, m;
int main()
{
int T;
cin >> T;
while (T--)
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> p[i];
int res = max(p[1], n - p[m] + 1);
for (int i = 1; i < m; i++)
res = max(res, (p[i] + p[i + 1]) / 2 + 1 - p[i]);
cout << res << endl;
}
return 0;
}
反思:没有理清题意,,想不到思路
第三题dp
原名:选取数对
题目意思:从数组中选出
k
k
k 个长度为
m
m
m 的不相邻区间,求
k
k
k个区间求和的最大值
思路:前缀和+dp
闫氏dp分析法:
状态表示
f[i][j]
表示在前i
个数中选取j
个区间的所有集合的结果数目。- 属性:最大值
状态计算(难点)
这里的 i
指的是右端点
那最后一个区间是确定的,右端点为i
,长度为m
那倒数第二个区间的右端点至少是 i-m
依次往前推
判断条件:第i
个区间是否选取
if(i - m < 0)
f[i][j] = max(f[i - 1][j],w[i])
else
f[i][j] = max(f[i - 1][j],f[i - m][j - 1] + w[i])
w[i]
是前缀和
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 5010;
typedef long long LL;
int n, m, k;
LL s[N], f[N][N];
int main()
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) //读取每个数 记录前缀和
{
int x;
scanf("%d", &x);
s[i] = s[i - 1] + x;
}
memset(f, -0x3f, sizeof f); //初始最大化数组
for (int i = 0; i <= n; i++) //初始化
f[i][0] = 0;
for (int j = 1; j <= k; j++)
{
LL max_f = 0;
for (int i = j * m; i <= n; i++)
{
max_f = max(max_f, f[i - m][j - 1] + s[i] - s[i - m]);
f[i][j] = max_f;
}
}
LL res = 0;
for (int i = 1; i <= n; i++)//这一步可以合并到上面的循环中
res = max(res, f[i][k]);
printf("%lld\n", res);
return 0;
}
反思:题目未读懂+dp不够熟练
加油,努力努力再努力~