A - 必做题 - 1
问题描述
给出n个数,zjm想找出出现至少(n+1)/2次的数, 现在需要你帮忙找出这个数是多少?
解题思路
读入每个数并记录出现次数,没给出数据范围,所以使用map映射
代码
#include <iostream>
#include <map>
using namespace std;
map<int, int> mp;
int main(){
int N;
while(cin >> N){
int temp, n = N, ans = 0;
while(N--){
cin >> temp;
mp[temp]++;
if(mp[temp] >= (n+1)/2)
ans = temp;
}
cout << ans << endl;
mp.clear();
}
return 0;
}
B - 必做题 - 2
问题描述
zjm被困在一个三维的空间中,现在要寻找最短路径逃生!
空间由立方体单位构成。
zjm每次向上下前后左右移动一个单位需要一分钟,且zjm不能对角线移动。
空间的四周封闭。zjm的目标是走到空间的出口。
是否存在逃出生天的可能性?如果存在,则需要多少时间?
解题思路
bfs的基本应用,从起点开始向6个方向bfs,直到终点为止。
代码
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int sx, sy, sz, ex, ey, ez;
int L, R, C;
char mp[31][31][31];
int ans[31][31][31];
bool vis[31][31][31];
int dx[] = { 0, 0, 1, -1, 0, 0 };
int dy[] = { 0, 0, 0, 0, 1, -1 };
int dz[] = { 1, -1, 0, 0, 0, 0 };
struct node {
int x, y, z;
node(int x, int y, int z): x(x) , y(y) , z(z) {}
};
void bfs()
{
memset(ans, 0, sizeof(ans));
memset(vis, 0, sizeof(vis));
vis[sz][sx][sy] = true;
queue<node> q;
node tmp(sx, sy, sz);
q.push(tmp);
while (!q.empty()) {
tmp = q.front();
q.pop();
for(int i = 0; i < 6; i++) {
int x = tmp.x + dx[i], y = tmp.y + dy[i], z = tmp.z + dz[i];
if(x >= 0 && x < R && y >= 0 && y < C && z >= 0 && z < L){
if(mp[z][x][y] == '.' && !vis[z][x][y]){
vis[z][x][y] = true;
ans[z][x][y] = ans[tmp.z][tmp.x][tmp.y] + 1;
node tmp1(x, y, z);
q.push(tmp1);
}
}
}
}
if(vis[ez][ex][ey])
cout << "Escaped in " << ans[ez][ex][ey] << " minute(s).\n";
else
cout << "Trapped!\n";
}
int main()
{
while (cin >> L >> R >> C) {
if (L == 0 && R == 0 && C == 0)
break;
for (int i = 0; i < L; i++)
for (int j = 0; j < R; j++)
for (int k = 0; k < C; k++) {
cin >> mp[i][j][k];
if (mp[i][j][k] == 'S') {
sx = j;
sy = k;
sz = i;
} else if (mp[i][j][k] == 'E') {
ex = j;
ey = k;
ez = i;
mp[i][j][k] = '.';
}
}
bfs();
}
return 0;
}
C - 必做题 - 3
问题描述
东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人
这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j)
问题是要找到sum(i1, j1) + … + sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。
注:1 ≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)
解题思路
转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
+
a
[
j
]
,
d
p
[
i
−
1
]
[
k
]
+
a
[
j
]
;
(
i
−
1
<
k
<
j
)
dp[i][j] = max(dp[i][j-1] + a[j], dp[i-1][k] + a[j];(i - 1 < k < j)
dp[i][j]=max(dp[i][j−1]+a[j],dp[i−1][k]+a[j];(i−1<k<j)
由于数据较大,需要使用滚动数组优化。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
int a[1000005];
int dp[1000005];
int dp0[1000005];
int main(){
int m, n, max;
while(scanf("%d %d", &m, &n) != EOF){
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(dp, 0, sizeof(dp));
memset(dp0, 0, sizeof(dp0));
for(int i = 1; i <= m; i++){
max = -1e9;
for(int j = i; j <= n; j++){
dp[j] = std::max(dp[j-1] + a[j], dp0[j-1] + a[j]);
dp0[j-1] = max;
max = std::max(dp[j], max);
}
}
printf("%d\n", max);
}
return 0;
}
D - 选做题 - 1
问题描述
We give the following inductive definition of a “regular brackets” sequence:
the empty sequence is a regular brackets sequence,
if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
if a and b are regular brackets sequences, then ab is a regular brackets sequence.
no other sequence is a regular brackets sequence
For instance, all of the following character sequences are regular brackets sequences:
(), [], (()), ()[], ()[()]
while the following character sequences are not:
(, ], )(, ([)], ([(]
Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence.
Given the initial sequence ([([]])], the longest regular brackets subsequence is [([])].
解题思路
区间dp,设dp[i][j]为从i开始到j结束的区间里,最长合法括号序列。如果当前s[i]=’(’ && s[j]=’)’ 或者 s[i]=’[’ && s[j]=’]’,那么序列长度+2,dp[i][j]=dp[i+1][j-1]+2
还需要枚举k, i <= k <= j,计算dp[i][k]+dp[k][j],处理多个合法序列组合的情况
代码
#include <algorithm>
#include <cstring>
#include <string>
#include <iostream>
using namespace std;
int dp[105][105];
string str;
int main(){
while(cin >> str){
if(str == "end") break;
memset(dp, 0, sizeof(dp));
for(int i = str.length() - 1; i >= 0; i--){
for(int j = i + 1; j < str.length(); j++){
if((str[i] == '(' && str[j] == ')') || (str[i] == '[' && str[j] == ']'))
dp[i][j] = dp[i+1][j-1] + 2;
for(int k = i; k <= j; k++)
dp[i][j] = max(dp[i][k] + dp[k][j], dp[i][j]);
}
}
cout << dp[0][str.length() - 1] << endl;
}
return 0;
}
E - 选做题 - 2
问题描述
马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
解题思路
状压dp的题。
状态S:查询S中是否有编号为i的点:S&(1<<i);在状态中添加编号为i的点:S=S | (1<<i);在状态S中删除编号为i的点:S=S ^ (1<<i)。
令f[S]表示完成S作业集合后被扣的最少分数。c[x]是作业x完成所需时间,d[x]是作业x的ddl,sum[S]是作业集合S对应的总时间,tmp=max(sum[S]+c[x]-d[x],0)是作业x被扣的分数。
转移方程:f[S | (1<<x)]=f[S] + tmp。
最后递归输出完成作业的顺序
代码
#include <algorithm>
#include <cstring>
#include <string>
#include <iostream>
using namespace std;
struct homework{
string name;
int D, C;
}hw[20];
int f[40000];
int sum[40000];
int pre[40000];
void out(int s){
if(s == 0) return;
out(s ^ (1 << pre[s]));
cout << hw[pre[s]].name << endl;
}
int main(){
int T;
cin >> T;
while(T--){
int n;
cin >> n;
for(int i = 0; i < n; i++)
cin >> hw[i].name >> hw[i].D >> hw[i].C;
sort(hw, hw + n, [](const homework &a, const homework &b){
return a.name < b.name;
});
memset(f, 0x3f, sizeof(f));
memset(sum, 0, sizeof(sum));
f[0] = 0;
for(int S = 0; S <= (1 << n) - 1; S++){
for(int x = 0; x < n; x++){
if(!(S & (1 << x))){
int tmp = max(sum[S] + hw[x].C - hw[x].D, 0);
if(f[S | (1 << x)] > f[S] + tmp){
f[S | (1 << x)] = min(f[S | (1 << x)], f[S] + tmp);
sum[S | (1 << x)] = sum[S] + hw[x].C;
pre[S | (1 << x)] = x;
}
}
}
}
cout << f[(1 << n) - 1] << endl;
out((1 << n) - 1);
}
return 0;
}