第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)题解
第一次写题解,希望能尽快努力摆脱“小白”!
这场比赛是在放寒假不久之后在家打的第一场比赛,受个人状态以及环境影响,当然最主要的原因还是在于自身水平的不足,爆零了☹。补题的时候觉得其实至少应该可以做出四题的,嗐。
#I-买花
https://ac.nowcoder.com/acm/contest/11746/I
遍历K(K>1)即可,注意:输入输出
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll a[20];
for(ll i=1;i<=15;i++){
a[i]=pow(2,i)-1;
}
ll t;
scanf("%lld",&t);
while(t--){
ll n;
scanf("%lld",&n);
int flag=0;
ll ans;
for(ll k=2;k<=15;k++){
if(n%a[k]==0){
flag=1;
break;
}
}
if(flag) printf("YE5\n");
else printf("N0\n");
}
return 0;
}
# D-Seek the Joker I
https://ac.nowcoder.com/acm/contest/11746/D
读懂题目之后,可以发现这道题其实就是巴什博奕的简单变形。建议把几个经典的博弈都学一下:巴什博奕、尼姆博弈、威佐夫博弈等
(由于当时还没有系统的学习过博弈的知识,感觉每一次碰到博弈问题,都有点怕怕的,然后就容易把简单问题复杂化,继续努力吧!)
巴什博奕是最后取到最后一个物品者胜利,而此题取到最后一张牌的人失败。其实也就是取到倒数第二张牌的人一定胜利,否则失败。在原巴什博奕的基础上,把牌的总数减一即可!
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t,n,k;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
if((n-1)%(k+1))printf("yo xi no forever!\n");
else printf("ma la se mi no.1!\n");
}
return 0;
}
E-Seek the Joker II
https://ac.nowcoder.com/acm/contest/11746/E
跟D题一样为博弈论,此题为威佐夫博弈的变形
(当你对各种博弈都很熟悉清晰的时候,自然会很容易联想到的)
此题乌龟上方的x-1张牌即为第一堆物品,乌龟下方的n-x张牌即为第二堆物品。抽中乌龟的失败:将上述两堆物品先抽完的胜利,否则失败。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
cin>>t;
while(t--){
int n,x;
cin>>n>>x;
int a=min(x-1,n-x);
int b=max(x-1,n-x);
int c=b-a;
double r=(sqrt(5.0)+1)/2;
if(a==(int)(c*r)) printf("ma la se mi no.1!\n");
else printf("yo xi no forever!\n");
}
return 0;
}
F-成绩查询ing
https://ac.nowcoder.com/acm/contest/11746/F
一看这种题,立马想到用结构体,结果就T了o(╥﹏╥)o
所以一定要注意数据范围啊!
两种查询操作:1)输入同学姓名,输出该同学的成绩、学号和性别。姓名和成绩、学号、性别一一对应,但用结构体暴力查会T。于是用map(key与value一一对应)
2)输入成绩,输出考到此成绩的同学姓名,按字典序输出。因为要排序,所以用set最合适不过了,set自动去重并排序。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int n;
cin>>n;
set<string> s[120];
map<string,int>mp1;
map<string,int>mp2;
map<string,int>mp3;
for(int i=1;i<=n;i++)
{
string Name;
int xb,xh,cj;
cin>>Name>>cj>>xb>>xh;
s[cj].insert(Name);
mp1[Name]=cj;
mp2[Name]=xh;
mp3[Name]=xb;
}
int m;
cin>>m;
while(m--){
int q;
cin>>q;
if(q==1){
string t;
cin>>t;
cout<<mp1[t]<<" "<<mp2[t]<<" "<<mp3[t]<<endl;
}
else{
int k;
cin>>k;
set<string>::iterator it;
it=s[k].begin();
for(;it!=s[k].end();it++){
cout<<*it<<endl;
}
}
}
return 0;
}
C-上进的凡凡
https://ac.nowcoder.com/acm/contest/11746/C
求给定数组的非降序子数组的个数。
我们知道一个非降序数组的所有子数组均为非降序(其实所给样例也在给我们提示),且长度为n的数组的子数组个数为n(n+1)/2
将给定数组分成若干个非降序的数组(长度尽可能的长),并将每个非降序数组的子数组个数相加即可。
(数据范围大,暴力求解会T)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int a[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
ll ans=0;
ll t=0;
for(int i=1;i<=n;i++){
if(a[i]>a[i+1]||i==n){
t=i-t;//非降序数组的长度
ans+=(t*(t+1))/2;
t=i;
}
}
printf("%lld\n",ans);
return 0;
}
B-小宝的幸运数组
https://ac.nowcoder.com/acm/contest/11746/B
若给定数组某一子数组的总和能整除K,则该子数组即为幸运子数组,现求最长的幸运子数组的长度。
求某一子数组的总和:利用前缀和
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define inf 0x3f3f3f3f
typedef long long ll;
ll a[maxn];//记录前缀和取余结果
int l[maxn],r[maxn];//记录各余数出现的最小位置及最大位置
int num[maxn];//记录各余数的个数
int main()
{
int t,n,k;
scanf("%d",&t);
while(t--){
memset(a,0,sizeof(a));
memset(r,0,sizeof(r));
memset(num,0,sizeof(num));
for(int i=0;i<maxn;i++) l[i]=inf;
scanf("%d%d",&n,&k);
int flag=0;
l[0]=0;
num[0]=1;//余数为0时
for(int i=1;i<=n;i++){
ll x;
scanf("%lld",&x);
a[i]=(a[i-1]+x)%k;
l[a[i]]=min(l[a[i]],i);
r[a[i]]=max(r[a[i]],i);
num[a[i]]++;
if(num[a[i]]>=2) flag=1; //相同余数至少要有两个
}
if(!flag) printf("-1\n");
else{
int ans=0;
for(int i=0;i<maxn;i++){
if(num[i]>=2){
ans=max(ans,r[i]-l[i]);
}
}
printf("%d\n",ans);
}
}
return 0;
}
H-数羊
https://ac.nowcoder.com/acm/contest/11746/H
m只能取0、1、2;因此将三种情况都计算出来就好啦
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define inf 0x3f3f3f3f
typedef long long ll;
const int mod=998244353;
ll speed(ll a,ll b){//快速幂!!!
ll ans=1;
while(b){
if(b&1) ans=(ans*a)%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
int main()
{
ll t,n,m;
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
ll ans;
if(m==0){
ans=n+2;
}
else if(m==1){
ans=2*n;
}
else{
ans=speed(2,n);
}
printf("%lld\n",ans%mod);
}
return 0;
}
K-黑洞密码
https://ac.nowcoder.com/acm/contest/11746/K
注意读题,审清题意。
输入长度为32的仅有字母和数字组成的字符串。将其分为字母和数字两部分,每部分长度均为16,且两部分相同位置上的字母与数字一一对应。又将每部分分为四组,每组四个,依据转化规则转换后,颠倒顺序输出。
特别注意:‘z’之后是’B’,‘Z’之后是’b’;
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define inf 0x3f3f3f3f
typedef long long ll;
int main()
{
string s;
cin>>s;
vector<char>a[4];//存放四组字母
vector<int>b[4];//存放四组数字
int ca=0,cb=0,cnt1=0,cnt2=0;
for(int i=0;i<s.length();i++){
if(isalpha(s[i])){
ca++;
a[cnt1].push_back(s[i]);
if(ca%4==0) cnt1++;
}
else{
cb++;
b[cnt2].push_back(s[i]-'0');
if(cb%4==0) cnt2++;
}
}
int j;
for(int i=0;i<4;i++){
string ans;
for(int j=0;j<4;j++){
for(int k=0;k<b[i][j];k++){
if(a[i][j]=='Z') a[i][j]='b';//'Z'之后是'b'
else if(a[i][j]=='z') a[i][j]='B';//'z'之后是'B'
else a[i][j]=(char)(a[i][j]+1);
}
ans+=a[i][j];
}
reverse(ans.begin(),ans.end());//颠倒顺序
cout<<ans;
}
cout<<endl;
return 0;
}
J-这是一道简单的模拟
https://ac.nowcoder.com/acm/contest/11746/J
题如其名,这真的是一道简单的模拟!
数据范围很小,用二维数组存图即可。但需注意几点:
1)相邻城市存在通路;
2)恰好能都到达N个出差城市一次(不能漏掉!也不能重复!)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
#define inf 0x3f3f3f3f
int a[maxn][maxn];
int vis[maxn];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
a[i][j]=inf;
a[j][i]=inf;
}
}
while(m--){
int u,v,w;
cin>>u>>v>>w;
a[u][v]=w;
a[v][u]=w;
}
int k;
cin>>k;
int ans=inf;
while(k--){
int n;
cin>>n;
int x=0,y;
int flag=0,flag1=0;
int sum=0;
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++){
cin>>y;
vis[y]++;
if(a[x][y]!=inf){//相邻城市存在通路
sum+=a[x][y];
}
else{
flag=1;
}
x=y;
}
if(a[y][0]!=inf) sum+=a[y][0];
else flag=1;
for(int i=1;i<=n;i++){
if(vis[i]!=1){//每个城市恰好到达一次
flag1=1;
break;
}
}
if(!flag&&!flag1) ans=min(ans,sum);
}
if(ans!=inf) cout<<ans<<endl;
else cout<<"-1\n";
return 0;
}
L-建立火车站
求最大值的最小值:二分搜答案!
这个知识点我还不是特别理解,通过看别人的题解,懂了那么一点点,还需要做一些这方面的题,以加深我的理解、加强运用能力。
最终答案范围确定:所有城市坐标小于等于10^12,且不存在负值
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
long long a[MAXN];
int n,k;
long long judge(long long mid){
int m=0;
for(int i=2;i<=n;i++)
if(a[i]-a[i-1]>=mid){//两城市之间的距离大于等于mid时,才能在两城市之间建立临时停靠站
m+=(a[i]-a[i-1])/mid;//临时停靠站距离为mid时,在两城市之间能建立的停靠站个数
if((a[i]-a[i-1])%mid==0) m--;//若距离为两城市间距离的整数倍,则第一个临时停靠站的位置与原城市位置重复,需减一
}
if(m>k) return 0;//若此时能建立的停靠站的数量大于k,说明距离还太小了,需继续增大距离mid,故调整l
return 1;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
long long l=0;
long long r=1000000000000;
while(l<r){
long long mid=(l+r)/2;
if(judge(mid)==1)
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}