T1
题目描述
给出n个数,zjm想找出出现至少(n+1)/2次的数, 现在需要你帮忙找出这个数是多少?
Input
本题包含多组数据:
每组数据包含两行。
第一行一个数字N(1<=N<=999999) ,保证N为奇数。
第二行为N个用空格隔开的整数。
数据以EOF结束。
Output
对于每一组数据,你需要输出你找到的唯一的数。
Sample Input
5
1 3 2 3 3
11
1 1 1 1 1 5 5 5 5 5 5
7
1 1 1 1 1 1 1
Sample Output
3
5
1
求解思路
初始化数组,因为已知数据范围,可以直接将数字对应数组下标,数组中存储该下标数字出现的次数,当次数大于等于(n+1)/2时,输出该数字。
代码
#include<iostream>
#include<stdio.h>
using namespace std;
#define maxx 1000005
int n;
int arr[1000005];
int main()
{
while (scanf("%d", &n) != EOF)
{
for (int i = 1; i <= maxx; i++)
{
arr[i] = 0;
}
for (int i = 1; i <= n; i++)
{
int tmp;
cin >> tmp;
arr[tmp]++;
if (arr[tmp] == (n + 1) / 2)
{
cout << tmp << endl;
}
}
}
}
T2
题目描述
zjm被困在一个三维的空间中,现在要寻找最短路径逃生!
空间由立方体单位构成。
zjm每次向上下前后左右移动一个单位需要一分钟,且zjm不能对角线移动。
空间的四周封闭。zjm的目标是走到空间的出口。
是否存在逃出生天的可能性?如果存在,则需要多少时间?
Input
输入第一行是一个数表示空间的数量。
每个空间的描述的第一行为L,R和C(皆不超过30)。
L表示空间的高度,R和C分别表示每层空间的行与列的大小。
随后L层,每层R行,每行C个字符。
每个字符表示空间的一个单元。’#‘表示不可通过单元,’.‘表示空白单元。
zjm的起始位置在’S’,出口为’E’。每层空间后都有一个空行。
L,R和C均为0时输入结束。
Output
每个空间对应一行输出。
如果可以逃生,则输出如下
Escaped in x minute(s).
x为最短脱离时间。
如果无法逃生,则输出如下
Trapped!
Sample Input
3 4 5
S….
.###.
.##…
###.#
##.##
##…
#.###
####E
1 3 3
S##
#E#
0 0 0
Sample Output
Escaped in 11 minute(s).
Trapped!
求解思路
通过三维数组存储空间位置,采用bfs的方法从起点开始,向外扩展,取出队列头元素,判断当前元素的上下左右前后六个位置,若该位置没有到达过,则加入队列,循环该过程至队列为空,同时,每次找到没有到达过的位置时,记录该位置据起点的距离,即为父元素距离起点距离+1,若找到E,则输出Escaped in x minute(s),返回0,若队列为空,返回1表示没有找到,输出Trapped!
其思想类似于二维空间的迷宫问题。
代码
#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
char arr[31][31][31];
int state[31][31][31];
int L, R, C;
struct point
{
int sl, sr, sc;
};
point s;
int pl[] = { 1,-1,0,0,0,0 };
int pr[] = { 0,0,-1,1,0,0 };
int pc[] = { 0,0,0,0,-1,1 };
int solve()
{
queue<point> que;
state[s.sl][ s.sr][ s.sc] = 0;
que.push(s);
while (!que.empty())
{
point fa = que.front();
point tmp = que.front();
que.pop();
for (int i = 0; i < 6; i++)
{
tmp.sl =fa.sl+pl[i];
tmp.sr = fa.sr + pr[i];
tmp.sc = fa.sc + pc[i];
if (tmp.sl >= 0 && tmp.sl < L && tmp.sr >= 0 && tmp.sr < R && tmp.sc >= 0 && tmp.sc < C)
{
if (arr[tmp.sl][tmp.sr][tmp.sc] == 'E')
{
state[tmp.sl][tmp.sr][tmp.sc] = state[fa.sl][fa.sr][fa.sc] + 1;
cout << "Escaped in " << state[tmp.sl][tmp.sr][tmp.sc] << " minute(s)." << endl;
return 0;
}
if (arr[tmp.sl][tmp.sr][tmp.sc] == '.' && state[tmp.sl][tmp.sr][tmp.sc] == -1)
{
//cout << tmp.sl << tmp.sr << tmp.sc << endl;
state[tmp.sl][tmp.sr][tmp.sc] = state[fa.sl][fa.sr][fa.sc] + 1;
que.push(tmp);
}
}
}
}
return 1;
}
int main()
{
while (1)
{
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++)
{
state[i][j][k] = -1;
char tmp;
cin >> tmp;
// cout << tmp << endl;
arr[i][j][k] = tmp;
if (tmp == 'S')
{
s.sl = i;
s.sr = j;
s.sc = k;
}
}
// cout << "aa";
int con = solve();
if (con == 1)
{
cout << "Trapped!"<< endl;
}
}
}
T3
题目描述
东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有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)
Input
输入m,输入n。后面跟着输入n个ai
Output
输出最大和
Sample Input
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Sample Output
6
8
Hint
数据量很大,需要scanf读入和dp处理。
求解思路
如代码所示,动态规划求解时,k记录当前寝室数下的进行m次扫楼的结果,由!k更新而来,j表示进行段数,最大为m,最后一维记录当前段选与不选。
当段数小于寝室数时,动态规划方程为:
dp[k][j][1] = max(dp[!k][j][1] + value , max(dp[!k][j - 1][0] + value, dp[!k][j - 1][1] + value));
dp[k][j][0] = max(dp[!k][j][0], dp[!k][j][1]);
最终结果为max(dp[k][m][0], dp[k][m][1])
另外,初始化数组应该为负值,因为可能有负数,以及 初始条件 dp[0][0][0]=0;dp[0][0][1]=0; dp[1][0][0]=0;dp[1][0][1]=0;
代码
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;
long long dp[2][100005][2];
long long n, m;
int main()
{
// while (scanf("%lld",&m)!=EOF)
while (scanf("%lld%lld",&m,&n)!=EOF)
{
// scanf("%lld", &n);
long long k = 0;//滚动数组
for(int i=0;i<2;i++)
for(int j=0;j<=m;j++)
for(int p=0;p<2;p++)
{
dp[i][j][p] = -1e9;
}
dp[0][0][0]=0;dp[0][0][1]=0; dp[1][0][0]=0;dp[1][0][1]=0;
for (long long i = 1; i <= n; i++)
{
long long value;
scanf("%lld",&value);
// cin>>value;
k = !k;
for (long long j = 1; j <= m; j++)
{
if (j > i) break;
dp[k][j][1] = max(dp[!k][j][1] + value , max(dp[!k][j - 1][0] + value, dp[!k][j - 1][1] + value));
dp[k][j][0] = max(dp[!k][j][0], dp[!k][j][1]);
// cout << dp[k][j][1] << endl;
}
}
printf("%lld\n",max(dp[k][m][0], dp[k][m][1]));
}
}
T4
题目描述
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 [([])].
Input
The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters (, ), [, and ]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.
Output
For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.
Sample Input
((()))
()()()
([]])
)[)(
([][][)
end
Sample Output
6
6
4
0
6
求解思路
动态规划求解时,i代表左端点,j代表右端点,dp[i][j]表示[i,j]中含有的满足要求的括号数量。
动态规划方程为:
首先dp[i][j]至少含有其子序列含有的最大括号数量
for (int k = i; k < j; k++)
{
dp[i][j] =max(dp[i][j], dp[i][k]+dp[k + 1][j]);
}
另外,判断当前序列左右端点是否构成一对括号,若是,比较[i+1,j-1]中的括号数量+2和[i,j]中含有的满足要求的括号数量,取较大值。
if ((str[j] == ')'&&str[i]=='(')|| (str[j] == ']' && str[i] == '['))
{
dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
}
为了保证从小序列更新至大序列,所以j从左往右循环递增,i从当前j循环递减至0,这样就保证了每次更新时,用到的其他序列已经是更新过的。
代码
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string>
using namespace std;
int dp[105][105];
int main()
{
while (1)
{
string str;
cin >> str;
if (str == "end")
return 0;
for (int i = 0; i < 105; i++)
for (int j = 0; j < 105; j++)
dp[i][j] = 0;
for (int j = 0; j < str.size(); j++)
{
for (int i = j-1; i >= 0; i--)
{
for (int k = i; k < j; k++)
{
dp[i][j] =max(dp[i][j], dp[i][k]+dp[k + 1][j]);
}
if ((str[j] == ')'&&str[i]=='(')|| (str[j] == ']' && str[i] == '['))
{
dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
}
}
}
cout << dp[0][str.size() - 1] << endl;
}
}
T5
题目描述
马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
Input
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数 n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100) S 表示任务的名称和两个整数 D 和 C,分别表示任务的截止时间和完成任务需要的天数。
这 n 个任务是按照字符串的字典序从小到大给出。
Output
每组测试数据,输出最少扣的分数,并输出完成作业的方案,如果有多个方案,输出字典序最小的一个。
Sample Input
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3
Sample Output
2
Computer
Math
English
3
Computer
English
Math
Hint
在第二个样例中,按照 Computer->English->Math 和 Computer->Math->English 的顺序完成作业,所扣的分数都是 3,由于 English 的字典序比 Math 小,故输出前一种方案
求解思路
C数组记录完成任务需要的天数,D数组记录截止时间。
采用状态压缩dp的方法,f[k]表示当前k选择下扣掉的分数,比如k的二进制为111101,则表示完成1、3、4、5、6作业,未完成2作业时,扣的最小分数。k的范围为0~ (1 << n) - 1,也就是若有4个作业,则k为0~(1111)二进制
动态规划方程:
首先,当前k下任务完成需要的天数计算:
计算每一位,累加
int sum;
sum = 0;
for (int x = 0; x < n; x++)
{
if (k & (1 << x))
{
sum += C[x];
}
}
进而,对n个任务进行判断,tmp为当前k下完成p任务所扣掉的分数,比较原有的完成该任务所扣掉的分数与当前情况下完成p任务所扣掉的分数,取较小值,若当前情况下完成p任务所扣掉的分数更小,即发生了更新,则前驱情况记录到pre数组中,更新的任务记到pr数组中。
for (int p = 0; p < n; p++)
{
int tmp;
tmp = max(sum + C[p] - D[p], 0);
if (f[k] + tmp < f[k | (1 << p)])
{
f[k | (1 << p)] = f[k] + tmp;
pre[k | (1 << p)] = k;
pr[k | (1 << p)] = p;
}
}
最少扣的分数为f[maxlen],即当n个任务时,maxlen二进制为1……1(n个1)
采用递归的方式输出方案:
void output(int s)
{
if (pre[s] != -1)
{
output(pre[s]);
cout << name[pr[s]] << endl;
}
else
{
return;
}
}
代码
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string>
#include<math.h>
using namespace std;
int f[40000];
int pre[40000];
int pr[40000];
int C[20];
int D[20];
string name[20];
void output(int s)
{
if (pre[s] != -1)
{
output(pre[s]);
cout << name[pr[s]] << endl;
}
else
{
return;
}
}
int main()
{
int g;
cin >> g;
for (int i = 0; i < g; i++)
{
int n;
cin >> n;
for (int j = 0; j < n; j++)
{
name[j].clear();
cin >> name[j] >> D[j] >> C[j];
}
int maxlen = (1 << n) - 1;
//初始化
for (int k = 0; k < 40000; k++)
{
pre[k] = -1;
pr[k] = 0;
f[k] = 0xffff;
}
f[0] = 0;
for (int k = 0; k < maxlen; k++)
{
int sum;
sum = 0;
for (int x = 0; x < n; x++)
{
if (k & (1 << x))
{
sum += C[x];
}
}
for (int p = 0; p < n; p++)
{
int tmp;
tmp = max(sum + C[p] - D[p], 0);
if (f[k] + tmp < f[k | (1 << p)])
{
f[k | (1 << p)] = f[k] + tmp;
pre[k | (1 << p)] = k;
pr[k | (1 << p)] = p;
}
}
}
cout << f[maxlen] << endl;
output(maxlen);
}
}