CSP-Week12 签到题
T1-水题
给出n个数,zjm想找出出现至少(n+1)/2次的数, 现在需要你帮忙找出这个数是多少?
INPUT和输入样例
本题包含多组数据:
每组数据包含两行。
第一行一个数字N(1<=N<=999999) ,保证N为奇数。
第二行为N个用空格隔开的整数。
数据以EOF结束。
输入样例:
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
Output和输出样例
对于每一组数据,你需要输出你找到的唯一的数。
输出样例:
3
5
1
解题思路
遍历一遍找出现最多次数的数字即可
T1源代码
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
const int M=1e7;
int record[M];
int main()
{
int group=0;
int num=0;
while(cin>>group)
{
int max_time=0;
int max_num=0;
memset(record,0,M);
for(int i=0;i<group;i++)
{
scanf("%d",&num);
record[num]++;
if(record[num]>max_time)
{
max_time=record[num];
max_num=num;
}
}
printf("%d\n",max_num);
}
return 0;
}
T2-BFS板子
zjm被困在一个三维的空间中,现在要寻找最短路径逃生!
空间由立方体单位构成。
zjm每次向上下前后左右移动一个单位需要一分钟,且zjm不能对角线移动。
空间的四周封闭。zjm的目标是走到空间的出口。
是否存在逃出生天的可能性?如果存在,则需要多少时间?
INPUT和输入样例
输入第一行是一个数表示空间的数量。
每个空间的描述的第一行为L,R和C(皆不超过30)。
L表示空间的高度,R和C分别表示每层空间的行与列的大小。
随后L层,每层R行,每行C个字符。
每个字符表示空间的一个单元。’#‘表示不可通过单元,’.‘表示空白单元。
zjm的起始位置在’S’,出口为’E’。每层空间后都有一个空行。
L,R和C均为0时输入结束。
输入样例:
3 4 5
S….
.###.
.##..
###.#
#####
#####
##.##
##…
#####
#####
#.###
####E
1 3 3
S##
#E#
###
0 0 0
Output和输出样例
每个空间对应一行输出。
如果可以逃生,则输出如下
Escaped in x minute(s).
x为最短脱离时间。
如果无法逃生,则输出如下
Trapped!
输出样例:
Escaped in 11 minute(s).
Trapped!
解题思路
三维空间DFS,板子题。
T2源代码
#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
const int M=35;
int flag[M][M][M];
int vis[M][M][M];
int L,R,C;
struct point
{
int i;
int j;
int k;
};
point strart_p;
point end_p;
queue<point> q;
void clean_room()
{
strart_p.i=0;
strart_p.j=0;
strart_p.k=0;
for(int i=0;i<L;i++)
for(int j=0;j<R;j++)
for(int k=0;k<C;k++)
{
flag[i][j][k]=0;
vis[i][j][k]=-1;
}
}
char str[M];
void init()
{
for(int i=0;i<L;i++)
{
for(int j=0;j<R;j++)
{
scanf("%s",&str);
for(int k=0;k<C;k++)
{
if(str[k]=='#')
flag[i][j][k]=0;
else if(str[k]=='.')
flag[i][j][k]=1;
else if(str[k]=='S')
{
vis[i][j][k]=0;
strart_p.i=i;
strart_p.j=j;
strart_p.k=k;
}
else if(str[k]=='E')
{
flag[i][j][k]=1;
end_p.i=i;
end_p.j=j;
end_p.k=k;
}
}
}
if(i!=L-1)
getchar();
}
}
point point_new;
void BFS()
{
q.push(strart_p);
while(!q.empty())
{
int i=q.front().i;
int j=q.front().j;
int k=q.front().k;
q.pop();
if(flag[i+1][j][k]==1 && vis[i+1][j][k]==-1 && i<L-1)
{
vis[i+1][j][k]=vis[i][j][k]+1;
point_new.i=i+1;
point_new.j=j;
point_new.k=k;
q.push(point_new);
}
if(flag[i-1][j][k]==1 && vis[i-1][j][k]==-1 && i>0)
{
vis[i-1][j][k]=vis[i][j][k]+1;
point_new.i=i-1;
point_new.j=j;
point_new.k=k;
q.push(point_new);
}
if(flag[i][j+1][k]==1 && vis[i][j+1][k]==-1 && j<R-1)
{
vis[i][j+1][k]=vis[i][j][k]+1;
point_new.i=i;
point_new.j=j+1;
point_new.k=k;
q.push(point_new);
}
if(flag[i][j-1][k]==1 && vis[i][j-1][k]==-1 && j>0)
{
vis[i][j-1][k]=vis[i][j][k]+1;
point_new.i=i;
point_new.j=j-1;
point_new.k=k;
q.push(point_new);
}
if(flag[i][j][k+1]==1 && vis[i][j][k+1]==-1 && k<C-1)
{
vis[i][j][k+1]=vis[i][j][k]+1;
point_new.i=i;
point_new.j=j;
point_new.k=k+1;
q.push(point_new);
}
if(flag[i][j][k-1]==1 && vis[i][j][k-1]==-1 && k>0)
{
vis[i][j][k-1]=vis[i][j][k]+1;
point_new.i=i;
point_new.j=j;
point_new.k=k-1;
q.push(point_new);
}
}
}
int main()
{
cin>>L>>R>>C;
while(L!=0 || R!=0 || C!=0)
{
clean_room();
getchar();
init();
BFS();
if(vis[end_p.i][end_p.j][end_p.k]==-1)
{
printf("Trapped!\n");
}
else
{
printf("Escaped in %d minute(s).\n",vis[end_p.i][end_p.j][end_p.k]);
}
cin>>L>>R>>C;
}
return 0;
}
T3-线性DP
现在,我认为您已经在Ignatius.L的“最大和”问题中获得了AC。作为一个勇敢的ACMer,我们总是挑战自己,迎接更棘手的问题。现在,您面临着一个更困难的问题。
给定一个连续编号序列S 1,S 2,S 3,S 4内容S X,内容S Ñ(1≤X≤N≤1,000,000,-32768≤小号X ≤32767)。我们定义一个函数sum(i,j)= S i + … + S j(1≤i≤j≤n)。
现在给定一个整数m(m> 0),您的任务是查找m和i对和j对,它们使sum(i 1,j 1)+ sum(i 2,j 2)+总和(I 3,J 3)+ … + SUM(我中号,J 米)最大(I X ≤我ý ≤Ĵ X或I X ≤Ĵ ý ≤Ĵ X是不允许的)。
但是我很懒,我不想编写一个特殊的判断模块,所以您不必输出m对i和j,只需输出sum(i x,j x)(1 ≤x≤m)。
Input和输入样例
每个测试用例将以两个整数m和n开头,然后是n个整数S 1,S 2,S 3 … S n。
处理到文件末尾。
输入样例:
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Output和输出样例
一行输出上述最大和。
6
8
解题思路
题目来自HDU-1024,是一道经典的线性DP优化问题。
看到题目,可以想到的初步思路就是二维dp,dp[i][j]表示到第j个元素(选择第j个元素),选择了i段的最大值。a[j]表示输入的每个位置上的权值。
状态转移有下面两种情况:
1、选择该元素,且将该元素和前面的一段合并。该情况下的状态转移方程可以写成:
dp[i][j]=dp[i][j-1]+a[j]
2、选择该元素,且该元素单独存放。该情况下,dp[i][j]应为遍历所有选择i-1段中的最大值+a[j],状态转移方程可以写成:
dp[i][j]=max(dp[i-1][k](0<k<j))+a[j]
使用这种求解方式,可以解决小数据的问题。但是由于元素个数达到了1e6,O(n3)的算法显然无法解决问题,而且需要的空间太大,栈都可能会爆掉,需要找到其他的优化方式。
优化一:滚动数组减掉一个维度
关注两个状态转移方程,可以发现明显的特点:
dp[i][j]=dp[i][j-1]+a[j]
dp[i][j]=max(dp[i-1][k](0<k<j))+a[j]
所有 i 维的数据均通过 i-1 或者 i 维更新完成,这种情况下就可以使用背包问题中的滚动数组,一维即可解决。
优化二:使用数组存放前置已有的最优解来优化时间
使用了滚动数组,可以将空间复杂度优化到可行,但是随即而来的是TLE,时间性能仍然不够优秀。分析算法之后我们可以发现,主要计算集中在三个循环:
i 循环------更新选择的段数
j 循环------更新每个位置上的最大值
k 循环-----找到前 j-1 个位置上的最大值
突然我们发现一个问题,在跑 k重循环之前,这个最大值我们在上一个循环已经计算出来了啊!只不过当时我们没有记录他就被更新掉了。因此加上一个记录每个位置及其之前的最大值数组,通过维持最大值数组,就可以同时满足时间和空间复杂度的需求。
T3实现源代码
#include<iostream>
#include<stdio.h>
using namespace std;
const int M=1e6+5;
int dp[M];
int last_dp[M];
//dp[i][j]表示到J个为止选取j,i段的最大值
int a[M];
int m;
int n;
const int INF=-1e8;
int maxx=0;
int main()
{
while(~scanf("%d %d",&m,&n))
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=0;i<=n;i++)
{
dp[i]=0;
last_dp[i]=0;
}
for(int i=1;i<=m;i++)
{
maxx=INF;
for(int j=i;j<=n;j++)
{
//两种情况:
int ans1=dp[j-1]+a[j];
int ans2=last_dp[j-1]+a[j];
dp[j]=max(ans1,ans2);
last_dp[j-1]=maxx;
maxx=max(maxx,dp[j]);
}
}
printf("%d\n",maxx);
}
}