NEUQ-ACM图灵杯第九届真题重现题解

用于辅助备战第十届的同学

A.G1024(*800)

考查:暴力

分析:对每趟列车剩余的票数求和,注意不要忘了票不够输出“G!

Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,k,x,y;
	int a[1010];
	cin>>n>>k;
	for(int i=1;i<=k;i++)
	{
		cin>>x>>y;
		a[i]=y-x;
	}
	int sum=0;
	for(int i=1;i<=k;i++)
	{
		sum+=a[i];
		if(sum>=n)
		{
			cout<<i;
			return 0;
		}
	}
	cout<<"G!";
}

B.NEUQ(*800)

考查:字符串

分析:我们从左到右遍历字符串,对"NEUQ"依次进行匹配,用ans记录成功匹配的次数。如果正好匹配到NEUQ的最后一位,答案就是n-ans;如果没匹配完,还需要考虑匹配到一半的字符串。

Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	string str="NEUQ";
	string s;
	cin>>s;
	int cnt=0,ans=0;
	for(int i=0;i<n;i++)
	{
		if(cnt==4)  cnt=0;
		if(s[i]==str[cnt])
		{
			ans++;
			cnt++;
		}
	}
	if(cnt==4)  cnt=0;
	cout<<n-ans+cnt;
}

C.小G的任务(*800)

考查:暴力

分析:对1~n的数求各位数字加和的加和(),n<=1000000,直接暴力求不会超时

Code:

#include<bits/stdc++.h>
using namespace std;
int f(int x)
{
	int sum=0;
	while(x)
	{
		sum+=x%10;
		x/=10;
	}
	return sum;
}
int main()
{
	int n;
	cin>>n;
	long long sum=0;
	for(int i=1;i<=n;i++)  sum+=f(i);
	cout<<sum;
}

D.nn与游戏(*1100)

考查:BFS

分析:对于每个控制单位而言,其它的控制单位和除了自己对应的敌对单位都是障碍物,所以对于每组单位坐标输入,我们需要先记录所有单位的位置,然后一一处理每个单位(只有10组,不用担心超时)。用vis数组先将所有的单位进行标记,对某一组单位进行处理时,只需要将其对应的敌对单位取消标记,然后BFS即可。处理下一组单位时重复上述操作。陷阱在地图上是永远存在的,对于陷阱直接在地图上标记即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int dirx[4]={-1,0,1,0},diry[4]={0,1,0,-1};
struct node{int x,y,dis;};
struct trap{int x,y;};
struct query{int dx,dy,zx,zy;}q[11];
int n,m,t;
char s[1010][1010];
int vis[1010][1010];
bool check(int x,int y)
{
	if(x<0||x>=n||y<0||y>=n||s[x][y]=='#'||vis[x][y])  return false;
	return true;
}
int bfs(int dx,int dy,int zx,int zy)
{
	queue<node>q;
	node start,next;
	start.x=dx,start.y=dy,start.dis=0;
	vis[dx][dy]=1;
	q.push(start);
	while(!q.empty())
	{
		start=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			next.x=start.x+dirx[i];
			next.y=start.y+diry[i];
			if(check(next.x,next.y))
			{
				next.dis=start.dis+1;
				if(next.x==zx&&next.y==zy)  return next.dis;
				vis[next.x][next.y]=1;
				q.push(next);
			}
		}
	}
	return -1;
}
int main()
{
	cin>>n>>m;
	memset(s,'.',sizeof(s));
	while(m--)
	{
		trap tr;
		cin>>tr.x>>tr.y;
		s[tr.x][tr.y]='#';
	}
	cin>>t;
	for(int i=0;i<t;i++)
		cin>>q[i].dx>>q[i].dy>>q[i].zx>>q[i].zy;
	for(int i=0;i<t;i++)
	{
		memset(vis,0,sizeof(vis));
		for(int j=0;j<t;j++)
		{
			vis[q[j].dx][q[j].dy]=1;
			vis[q[j].zx][q[j].zy]=1;
		}
		vis[q[i].zx][q[i].zy]=0;
		cout<<bfs(q[i].dx,q[i].dy,q[i].zx,q[i].zy)<<'\n';
	}
}

E.第二大数(*900)

考查:暴力

题意:先从下标1开始,求a1~a2之间第二大的数,然后求a1~a3之间第二大的数......依次类推到a1~an;然后从下标2开始,求a2~a3之间第二大的数......最后是求an-1~an之间第二大的数。对这些第二大数进行求和。

分析:这道题N的范围为10000,可以尝试用暴力的方法过。在下标区间[i,j-1]之间,有最大值maxx和第二大数se,如果aj>maxx,那么最大值就变成了aj,maxx就变成了第二大数;如果aj<maxx但aj>se,那么aj就变成了第二大数。按照这种方式持续更新最大值和第二大数即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int a[10010];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)  cin>>a[i];
	long long sum=0;
	for(int i=1;i<n;i++)
	{
		int maxx=a[i],se=0;
		if(a[i+1]>a[i])
		{
			maxx=a[i+1];
			se=a[i];
		}
		else
		{
			maxx=a[i];
			se=a[i+1];
		}
		for(int j=i+1;j<=n;j++)
		{
			if(a[j]>maxx)
			{
				se=maxx;
				maxx=a[j];
			}
			else if(a[j]<maxx&&a[j]>se)  se=a[j];
			sum+=se;
		}
	}
	cout<<sum;
}

F.Num(*1000)

考查:数学

分析:对于等式N=a*b+a+b,左右两边加1,就变成了N+1=a*b+a+b+1=(a+1)*(b+1),即要判断N是否满足条件,我们需要找一个大于1的数i(因为a>0,b>0),使(N+1)%i=0,即判断N+1是否是质数。(注意N=3需要特判)

Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	if(n==1||n==2)  cout<<"No";
	else if(n==3)  cout<<"Yes";
	else
	{
		int flag=0;
		for(int i=2;i<n;i++)
		{
			if((n+1)%i==0)
			{
				flag=1;
				break;
			}
		}
		if(flag)  cout<<"Yes";
		else  cout<<"No";
	}
}

G.特征值(*1000)

考查:前缀和

题意:假设给定一个数为1225,我们需要求出1225+122+12+1;对于99999,我们需要求出99999+9999+999+99+9

分析:数据范围极大,但是用字符串表示数却刚刚好。观察上述的计算过程,我们可以发现,计算的特点是每一个加数都是各位数字的前缀和。因此,我们将整个字符串转化成数字后求前缀和,再进行加法竖式运算模拟即可。

下面以字符串"1225"举例,前缀和数组为s[N]

  1 2 2 5                  第4位:(5+2+2+1)%10=s[4]%10=0,进位add=s[4]/10=1

     1 2 2                  第3位:(2+2+1+add)%10=(s[3]+add)%10=6,进位add=(s[3]+add)/10=0

        1 2                  第2位:(2+1+add)%10=(s[2]+add)%10=3,进位add=(s[2]+add)/10=0

+         1                  第1位:(1+add)%10=(s[1]+add)%10=1,进位add=(s[1]+add)/10=0

————                由于add=0,说明最后第1位不需要进位

  1 3 6 0                 如果add>0,还需要在第1位之前填充上add

Code: 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	string s;
	int a[500010];
	cin>>s;
	int n=s.length();
	a[0]=0;
	for(int i=1;i<=n;i++)  a[i]=a[i-1]+s[i-1]-'0';
	stack<int>stk;
	int add=0;
	for(int i=n;i;i--)
	{
		stk.push((a[i]+add)%10);
		add=(a[i]+add)/10;
	}
	if(add)  stk.push(add);
	while(!stk.empty())
	{
		cout<<stk.top();
		stk.pop();
	}
}

H.最大公因数(*1000)

考查:思维

题意:给出一个序列,你可以选择两个数,令其中一个数减去1,另一个数加1,可以无限次操作。问你最多能得到多少个不同的整个序列的最大公因数。

分析:本题的特点是,选择L和R的时候,不必令L<=R;无论怎么变换,数列的总和不变;规定任何正整数都是0的公因数。假设有n个加和为sum的数,我们可以把前n-2个数全变成0,只对最后两个数进行操作。操作时我们可以发现,当其中一个数是sum的因子时,它也是两个数的最大公因数(假设两个正整数a和b,如果a为sum的因子,那么存在一个正整数N,使得a*N=sum,b=sum-a=a*(N-1),a和b的最大公因数便是a,更严谨的证明可以看官方题解)。因此,答案就是sum的因子数。

Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,sum=0,a[1010],ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	for(int i=1;i<=sum;i++)
		if(sum%i==0)  ans++;
	cout<<ans;
}

I.玄神的字符串(*1100)

考查:贪心

分析:删除的规则与位置无关,只与0和1的数量有关。操作3不能删除字符,因此操作3是为操作1和2服务的。由于字符串长度为偶数,因此只有两种情况:0和1的数量都是偶数,或者都是奇数。假设0的数量x少于1的数量y:

(1)x和y是偶数

如果主要进行操作1:

方案1:先一直进行操作1,对于剩下的偶数数量的1,先将其中的一半变成0后再用操作1,花费:

x*a+1/2*(y-x)*c+1/2*(y-x)*a=x*a+1/2*(y-x)*(a+c)

方案2:先一直进行操作1,对于剩下的1全部执行操作2

x*a+1/2*(y-x)*b

如果主要进行操作2:

方案1:一直选择两个相同的字符一起删,花费:1/2*x*b+1/2*y*b=1/2*n*b

方案2:先删x个0和x个1,将剩下一半数量的1变成0后,用操作1,花费:

x*b+1/2*(y-x)*c+1/2*(y-x)*a=x*b+1/2*(a+c)*(y-x)

(2)x和y是奇数

如果主要进行操作2:

方案1:先一直进行操作2,最后一定会剩下"01",对其之间进行操作1,花费:1/2*(n-2)*b+a

方案2:先一直进行操作2,对剩下的01先变换一个数,再执行操作2,花费:1/2*(n-2)*b+c+b

如果主要进行操作1:

方案1:先一直进行操作1,最后一定剩下偶数个1,对其全部进行操作2,花费:

x*a+1/2*(y-x)*b

方案2:先一直进行操作,将剩下一半的1变换后执行操作1,花费:

x*a+1/2*(y-x)*c+1/2*(y-x)*a=x*a+1/2*(y-x)*(a+c)

如果1的数量大于等于0的话也是同理。

针对不同的情况对上述四种花费取最小值即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int ans[4];
	int cnt=0,cnt1=0;
	string s;
	int a,b,c;
	cin>>s;
	cin>>a>>b>>c;
	int n=s.length();
	for(int i=0;i<n;i++)
		if(s[i]=='0')  cnt++;
		else  cnt1++;
	if(cnt%2==0)
	{
		ans[0]=n*b/2;
		ans[1]=(max(cnt,cnt1)-min(cnt,cnt1))*(a+c)/2+min(cnt,cnt1)*b;
		ans[2]=min(cnt,cnt1)*a+(max(cnt,cnt1)-min(cnt,cnt1))*(a+c)/2;
		ans[3]=min(cnt,cnt1)*a+(max(cnt,cnt1)-min(cnt,cnt1))*b/2;
	}
	else
	{
		ans[0]=(n-2)*b/2+a;
		ans[1]=(n-2)*b/2+c+b;
		ans[2]=min(cnt,cnt1)*a+(max(cnt,cnt1)-min(cnt,cnt1))*(a+c)/2;
		ans[3]=min(cnt,cnt1)*a+(max(cnt,cnt1)-min(cnt,cnt1))*b/2;
	}
	sort(ans,ans+4);
	cout<<ans[0];
}

J.金牌厨师(*1600)

考查:二分答案

后续更新

K.WireConnection(*1400)

考查:最小生成树

分析:因为给出的是点坐标,因此我们需要将各个点连接的边进行存储,然后采用kruskal算法即可。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2010;
int n,p[N];
int find(int x)
{
	if(p[x]!=x)  p[x]=find(p[x]);
	return p[x];
}
struct point{ll x,y,z;}points[N*N];
struct edge
{
	int a,b;
	ll w;
    bool operator<(const edge &W)const
    {
        return w<W.w;
    }
}edges[N*N];
double getdis(double x1,double y1,double z1,double x2,double y2,double z2)
{
	return ceil(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)));
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)  cin>>points[i].x>>points[i].y>>points[i].z;
	int m=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i!=j)
			{
				m++;
				edges[m].a=i;
				edges[m].b=j;
				edges[m].w=getdis(points[i].x,points[i].y,points[i].z,points[j].x,points[j].y,points[j].z);
			}
		}
	}
	sort(edges+1,edges+m+1);
	for(int i=1;i<=n;i++)  p[i]=i;
	ll res=0;
	for(int i=1;i<=m;i++)
	{
		int a=edges[i].a,b=edges[i].b,w=edges[i].w;
		a=find(a),b=find(b);
		if(a!=b)
		{
			p[b]=a;
			res+=w;
		}
	}
	cout<<res;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值