注:这次模拟赛的出题人为:LJH,OQL,DB。
测试
By rakish oiers
中文题目名称 | 思胜学数数 | 思胜学小数 | 简单的排序 | 期末考试 | 思胜迷路了 | 思胜去旅行 |
英文题目 | count | ssxxs | sort | test | lose | travel |
输入文件名 | count.in | ssxxs.in | sort.in | test.in | lose.in | travel.in |
输出文件名 | count.out | ssxxs.out | sort.out | test.out | lose.out | travel.out |
时限 | 2s | 0.1s | 1.5s | 0.5s | 0.1s | 1s |
测试点数目 | 20 | 100 | 10 | 10 | 10 | 10 |
每个测试点分值 | 5 | 1 | 10 | 10 | 10 | 10 |
结果比较方式 | 全文比较(忽略行末空格及回车) | |||||
题目类型 | 传统 | 传统 | 传统 | 传统 | 传统 | 传统 |
内存上限 | 128M | 128M | 128M | 128M | 128M | 128M |
思胜学数数 (count)2s
思胜是一个神奇的信息学老师,但他的知识水平并不能【压制】那些爱装13的学生(比如说煎堆)。为了好好教他的学生学习二进制,他决定自己先研究二进制数字。那么问题来了?二进制怎么表示呢?不穿鞋的思胜决定在泥地上按印子来表示!在泥地上,思胜用印子表示0,用自己的手(或脚)表示1,这样子他最多能表示四个位置上的1。
现在我们给定思胜的教学范围[s,t],问思胜最多能表示出多少个数字。
【输入格式】
一行仅两个数,s和t,表示一个范围[s,t]。
【输出格式】
一行仅一个数,表示思胜在区间[s,t]中能表示多少个数字。
【样例输入】
103 108
【样例输出】
4
【样例解释】
数字 | 二进制数字 | 1'的个数 | 思胜能否表示 |
103 | 1100111 | 5 | NO |
104 | 1101000 | 3 | YES |
105 | 1101001 | 4 | YES |
106 | 1101010 | 4 | YES |
107 | 1101011 | 5 | NO |
108 | 1101100 | 4 | YES |
【数据范围】
50% 1<=s,t<=1,000,000
100% 1<=s, t<=30,000,000
【题意分析】
求一个区间内每个整数在二进制表示下,有多少个数字1,如果数字1的个数小于等于4答案加一。
【解题思路】
首先来看看这道题怎么做,显然,放在第一题绝壁是水题,所以我们直接想暴力。
如何求一个数的二进制?呵呵,不会的右转女厕所度娘。
就是一直mod2,得到一个余数,再把原数/2,直到原数为0。
计算时间复杂度,一共3*10^7,再乘一个最大二进制长度是25位,看起来显然是会爆掉的,但实际上,我们可以加一个退出判断,如果找到1的个数大于4就退出,这样复杂度大概是
O(4n)多,时限是两秒,所以并不会爆,而且我们除以二的过程还可以是用位运算,增加运算速度。
【程序】
#include<iostream>
#include<cstdio>
using namespace std;
int s,t,ans;
bool check(int num)
{
int sum=0;
while(num!=0)
{
if(num&1)
++sum;
if(sum>4)
return false;
num>>=1;
}
return true;
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%d%d",&s,&t);
for(int i=s;i<=t;++i)
{
if(check(i))
++ans;
}
printf("%d\n",ans);
return 0;
}
思胜学小数(ssxxs.cpp)
最近,思胜在学习有关小数的知识。他发现,当某些整数相除是,所得的商是以个“循环小数”。何为循环小数,就是循环节有有限长度的无限小数。例如:“3.33333”,可以表达成
现在思胜给出两个整数A和B,让你求出A/B的商的循环节的长度。
| 输入 Input | 输出 Output |
例子一 | 10 3
| 1
|
例子二 | 5 7
| 6 |
数据范围:0<A,B<=10^7
【题意分析】
应该就不用分析了吧……
【解题思路】
每次求模求小数位数,直到求模后的数出现两次。
【程序】
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
map<int,int> f;
int a,b,c;
int main()
{
freopen("ssxxs.in","r",stdin);
freopen("ssxxs.out","w",stdout);
scanf("%d%d",&a,&b);
for(int i=1;i<=10000000;i++)
{
c = a % b;
if(f[c])
{
printf("%d",i-f[c]);
return 0;
}
f[c] = i;
a = c * 10;
}
return 0;
}
T3 简单的排序(sort) 1.5s
思胜曰:“在这个世界上,有八种基本排序,他们分别是……不好意思我忘了。”
思胜这番话遭到了他的学生的嘲讽,于是思胜想出一道“简单的排序题”让他的学生做,以表示自己的威严。思胜不想他的学生能够很容易就把题目做出来,所以他决定出一道他的学生没有见过的排序题。
思胜觉得如果只把数字排序太简单了!于是他决定按数字的前k位来排序,如果前k位相同,就按后面位数的逆序排序!当然,和普通的数字比较一样,两个数字在比较时,长的数字比较大。又由于思胜不想太麻烦,于是他没有加入负数,但他认为这已经足够了!
思胜说:“用字符串来做就好了啊!”嗯,信不信就是你的事了!
【输入格式】
第一行两个数字分别是n,k。
接下来n行每行有一个数字,表示需要排序的数字。
【输出格式】
n行,每行仅一个数字,按数字的从大到小排列输出。
【样例输入】
5 3
123886
79
123867
88888
321584
【样例输出】
321584
123867
123886
88888
79
【样例解释】
小样~就是不给你解释~
【数据范围】
20% 1<=k,n<=100
50% 1<=k,n<=1,000,000
100% 1<=k,n<=2,000,000
对于每一个数字保证在C++的int范围内
【题意分析】
就是排序题目,题目说的没错,只不过是多关键字排序。
【解题思路】
题目说是排序,就是排序咯。
首先来看看20%的数据,就是送给那些sort都不会的小朋友,一个一个进行比较,暴力。具体做法是冒泡O(n^2),然后每两个字符串一位一位比较O(n),和一起就O(n^3)。
然后是50%的数据,送给那些被我坑的小朋友。
题目说了可以用字符串做,我想说:题目说什么你就什么啊,我还说题目是用【在线仙人球嵌套动态网络路径剖分优化的分支定界贪心剪枝启发式迭代加深人工智能搜索决策算法】来做呢。
这样子sort是O(nlogn),两两比较时O(len),最大为8,所以是O(8n log(n)),会超时。
100%的数据,用数字做,和字符串做法一样,就是时间复杂度少一个len而已,就是这一点点的差距,决定50分的归属。
【程序】
#include<iostream>#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
const int MAXN=2000007;
int n,k;
struct Tnum
{
int plon,pre,suf;
char st[13];
};
Tnum num[MAXN];
void change(int ps)
{
int len=strlen(num[ps].st);
num[ps].plon=len;
num[ps].suf=num[ps].pre=0;
for(int i=0;i<min(k,len);++i)//make pre
{
num[ps].pre*=10;
num[ps].pre+=(num[ps].st[i]-'0');
}
if(k>=len)
return;
for(int i=len-1;i>=k;--i)
{
num[ps].suf*=10;
num[ps].suf+=(num[ps].st[i]-'0');
}
}
int cmp(Tnum Ta,Tnum Tb)
{
if(Ta.plon==Tb.plon)
{
if(Ta.pre==Tb.pre)
return Ta.suf>Tb.suf;
return Ta.pre>Tb.pre;
}
return Ta.plon>Tb.plon;
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=0;i<n;++i)
{
getchar();
scanf("%s",num[i].st);
change(i);
}
sort(num+0,num+n,cmp);
for(int i=0;i<n;++i)
printf("%s\n",num[i].st);
return 0;
}
期末考试
(test.pas/cpp/c)
期末考试到了。思胜要考考tc对于数据结构过不过关。于是,SB思胜要tc猜猜数据结构。思胜给了tc两种操作:
1, push x——把元素x放进数据结构。
2, pop——把元素从数据结构中取出来。
思胜给了tc q种操作。让他猜猜这是队列还是栈。
tc不知道应该怎么做,于是来请你这位高手帮tc完成任务。
【输入格式】
输入有多组数据(数据以q=0结束)。对于每组数据:
第1行:1个整数q。
第2到第q+1行:一种操作。其中push x表示把元素x放进数据结构,pop x表示把元素从数据结构中取出来且这个元素是x。
【输出格式】
对于每组数据:
假如这是队列,输出”queue”。
假如这是栈,输出”stack”。
假如队列和栈都有可能,输出”notsure”。
假如都不可能,输出”no”。
【输入样例】
6
push 4
push 1
push 5
pop 4
pop 1
pop 5
4
push 1
pop 1
push 1000
pop 1000
8
push 1
push 2
pop 1
push 4
pop 4
pop 2
push 3
pop 5
0
【输出样例】
queue
not sure
no
【数据范围】
1<=数据组数<=5
1<=q<=100000
第四题:
【题意分析】
给q种操作,判断是队列还是栈。
【解题思路】
对于每组数据:都用栈和队列(可以用STL中的栈和队列)来模拟,判断是否正确就可以了。注意栈和队列要非空,不然会爆掉。
【程序】
#include <cstdio>
#include <queue>
#include <cstring>
#include <stack>
using namespace std;
int a[100010] , b[100010];
int n;
bool Stack()
{
stack <int> s;
for(int i=1;i<=n;i++)
if(a[i] == 1)
{
s.push(b[i]);
}
else
{
if(s.empty()) return 0;
if(s.top() != b[i]) return 0;
s.pop();
}
return 1;
}
bool Queue()
{
queue <int> q;
for(int i=1;i<=n;i++)
if(a[i] == 1)
{
q.push(b[i]);
}
else
{
if(q.empty()) return 0;
if(q.front() != b[i]) return 0;
q.pop();
}
return 1;
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
char s[6];
while(scanf("%d",&n) && n)
{
for(int i=1;i<=n;i++)
{
scanf("%s%d",s,&b[i]);
if(s[1]=='u') a[i] = 1;
else a[i] = 0;
}
bool r1 = Stack() , r2 = Queue();
if((r1) && (!r2)) printf("stack\n");
else if((!r1) && (r2)) printf("queue\n");
else if((r1) && (r2)) printf("not sure\n");
else printf("no\n");
}
return 0;
}
思胜迷路了
(lose.pas/cpp/c)
有一天,思胜和他的儿子去游乐场玩。他们从早上8点一直玩到傍晚6点。准备走的时候,思胜的儿子突然说:“我要去走迷宫,我要去!”。思胜无可奈何地陪他儿子去玩了。玩着玩着,思胜的儿子突然不见了!思胜在迷宫里乱撞,还是找不到他的儿子,可是——
思—胜—迷—路—了!
原来,他的儿子已经到达了终点,而他却在慌乱之中又来到了起点。
现在,思胜想用最快的方式找到孩子。思胜每秒钟走一个格子。现在,给出迷宫,起点和终点(很明显,思胜不能走出迷宫),请你告诉思胜最快要多长时间才能到达终点。假如到达不了终点则输出-1。
【输入格式】
共n+1行。
第1行:两个整数n,m。分别表示迷宫的长和宽。
第2~第n+1行:每行m个整数,表示迷宫。其中’S’为起点,’T’为终点,’$’为可通行的路,’#’为障碍。
【输出格式】
一行:最少秒数。
【输入样例1】
4 5
S$$$#
#$#$$
#$###
#$$$T
【输出样例1】
7
【输入样例2】
4 4
S###
$$$$
$$$#
$$#T
【输出样例2】
-1
【数据范围】
对于100%的数据:n,m<=6
第五题:
【解题思路】
宽搜,广搜,深搜,动态规划,最短路径……都可以。做不对的是智障!!!
【程序】
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define Maxn 10
#define INF 0x7ffffff
int ans = INF;
bool bo[Maxn][Maxn];
bool a[Maxn][Maxn];
int f1[4]={0,0,1,-1};
int f2[4]={1,-1,0,0};
int n,m;
int sx,sy;
int tx,ty;
void dfs(int x,int y,int k)
{
if(x == tx && y == ty)
{
ans = min(ans , k);
if(k == abs(tx - sx) + abs(ty - sy))
{
printf("%d\n",k);
exit(0);
}
return ;
}
bo[x][y] = true;
for(int i=0;i<4;i++)
{
int x1 = x + f1[i];
int y1 = y + f2[i];
if(x1 > 0 && x1 <= n && y1 > 0 && y1 <= m && a[x1][y1] && (!bo[x1][y1]))
dfs(x1,y1,k+1);
}
bo[x][y] = false;
}
int main()
{
freopen("lose.in","r",stdin);
freopen("lose.out","w",stdout);
scanf("%d%d",&n,&m);
char s[Maxn];
for(int i=1;i<=n;i++)
{
scanf("%s",s);
for(int j=0;j<m;j++)
{
if(s[j] == '#') a[i][j+1] = 0;
else a[i][j+1] = 1;
if(s[j] == 'S')
{
sx = i; sy = j + 1;
}
if(s[j] == 'T')
{
tx = i; ty = j + 1;
}
}
}
dfs(sx,sy,0);
if(ans == INF) ans = -1;
printf("%d\n",ans);
return 0;
}
T6 思胜去旅行 1s
思胜教了很久他的学生们,虽然他很爱他的学生,然而他的学生实在是太厉害了!他感到心力憔悴。于是思胜决定去各地旅游,放松心情。那么问题来了,思胜现在有n个想去的地方,这些地方都在一个一维坐标上面,每个地方的坐标分别是a1, a2, ..., an (1 ≤ ai ≤ 107) 。思胜一开始在原点,他想走遍这所有的点,显然他有很多种方案。现在思胜想计算出他每一种方案的总距离的和与总方案数的比值。其中,第i个点到第j个点的距离为
| ai-aj |
【输入格式】
第一行仅一个数n,表示思胜有n个想去的地方。
第二行有n个数,表示思胜每个想去的地方的坐标。
【输出格式】
一行仅两个数,
【样例输入】
3
2 3 5
【样例输出】
22 3
【样例解释】
方案1:2—>3—>5 路程:|0-2|+|2-3|+|3-5|=5
方案2:2—>5—>3 路程:|0-2|+|2-5|+|5-3|=7
方案3:3—>2—>5 路程:|0-3|+|3-2|+|2-5|=7
方案4:3—>5—>2 路程:|0-3|+|3-5|+|5-2|=8
方案5:5—>2—>3 路程:|0-5|+|5-2|+|2-3|=9
方案6:5—>3—>2 路程:|0-5|+|5-3|+|3-2|=8
总路程:44,总方案数:6
所以结果是44:6=22:3
故输出22和3
【数据范围】
10%n<=10
30%n<=1000
100%n<=100000,坐标在正轴上且不大于10^7
这一题就比较有意思了。是比较难的一题。
第六题:
【题意分析】
给定n个点,从原点出发,走遍所有的点(可以不按顺序走)。输出:所有的方案所行走的距离和与总方案数的比(最后的两个数需互质)。
【解题思路】
首先,先不要管数据范围——求总方案数是比较简单的,就是n!,为什么呢?首先,从0到每一个点,有n种方法。然后,剩下了(n-1)个点,因为不可能走回原点,所以还有(n-1)种走法……那么,根据乘法原理,一共有:n*(n-1)*(n-2)*……*1=n!种走法。
然后,我们来看看怎么推导所有方案所行走的距离。
1, 走第一步的时候,一共有n种走法,又因为共有n!种做法,所以,第一步从0~ai(0<i<=n)的走法分别有(n!)/n=(n-1)!种。
2, 下面是第2步到~第n步的走法。像ai~aj这样的走法,并使i,j相邻。除了这两个数以外,其他的排列就有(n-2)!种,那么,这样的i,j一共有(n-1)种走法。所以,一共有(n-2)!*(n-1)=(n-1)!种走法。假如还是不懂的话,看下面的例子:
有四个数字:1,2,3,4
选1,2:有(n-2)!种
选2,3:有(n-2)!种
……
共(n-1)种。
很明显,一共有(n-2)!*(n-1)=(n-1)!种走法。
因为有ai~aj,所以也可以有aj~ai,所以最终结果要*2。
3,接下来:我们要进行化简。
[(a1+a2+a3+……+an)*(n-1)!+2*(∑|ai-aj|(i!=j))*(n-1)!]/(n!)
=[a1+a2+a3+……+an+2*(∑|ai-aj|(i!=j))]/n
接下来:我们就要考虑计算∑|ai-aj|(i!=j)的问题。
其实,这个就相当于,给定n个点,求所有ai-aj的和。
4,再举一个例子:
L1=a2-a1 L2=a3-a2 L3=a4-a3
我们记T3为黄色的路线,T2为红色……。T则为∑|ai-aj|(i!=j)。
那么,T=T3+T2+T1
=(3L3+2L2+1L1)+(2L2+1L1)+(1L1)
=(1L1)+ (1L1+2L2)+(1L1+2L2+3L3)
很明显,T(i)=T(i-1)+i*Li
那么:可以用变量S把Ti计算出来,再用num把S累加就可以了(注意要乘二)。
如果按照上面的算法,则时间复杂度为O(n)。
5,
Ans = [(a1+a2+a3+……+an)+num]/n
则比为
(a1+a2+a3+……+an)+num:n
最后再用gcd它们的求最大公约数就可以了(记得用longlong)。
【程序】#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100007;
int n;
int dis[MAXN];
long long sum,ans,s;
long long gcd(long long x,long long y)
{
if(!(x%y))
return y;
return gcd(y,x%y);
}
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&dis[i]);
sum+=dis[i];
}
sort(dis+1,dis+n+1);
dis[0]=0;
for(int i=1;i<n;++i)
{
s+=(dis[i+1]-dis[i])*i;
ans+=(long long)s*2;
}
long long tgcd=gcd(ans+sum,n);
printf("%I64d %I64d\n",(ans+sum)/tgcd,n/tgcd);
return 0;
}