“九韶杯”河科院程序设计协会第一届程序设计竞赛(C++题解)

A题:6的个数

A题 提交链接

答案:602

代码:略

B题:小明的作业

B题 提交链接

答案:
78
25

代码:略微长

#include<stdio.h>
#include<string.h>
char a[110000];
char s[5]="aw";
int main()
{
	memset(a,'\0',sizeof(a));
	int i,j,la,f,w,x,ans1=0,ans2=0;
	scanf("%s",a+1);
	la=strlen(a+1);
	la++;
	a[la]='@';                  //关键不能少,内for的j一定会影响i的变化
	a[la+1]='\0';
	for(i=1;i<=la;i++)
	{
    	if(a[i]=='a')
    	{
        	w=0;
        	f=1;               //w,f用来数组s下标的变化
        	x=0;               //x用来连续aw的数量
        	for(j=i+1;j<=la;j++)
        	{
            	w+=f;
            	f=-f;
            	if(a[j]==s[w])
            	{
                	if(w==1)
                    	x++;
            	}
            	else
            	{
                	if(x>=2)
                    	ans2++;
                	else if(x==1)
                    	ans1++;
                	i=j-1;                  //当前字符要继续进行判断
                	break;
            	}      
        	}      
    	}
    	else if(a[i]=='w')
    	{
        	f=1;
        	w=1;
        	x=0;
        	for(j=i+1;j<=la;j++)
        	{
            	w-=f;
            	f=-f;
            	if(a[j]==s[w])
            	{
                	if(w==0)
                    	x++;
            	}
            	else
            	{
                	if(x>=2)
                    	ans2++;
                	else if(x==1)
                    	ans1++;
                	i=j-1;
                	break;
            	}
        	}
    	}
	}  
	printf("%d %d\n",ans1,ans2);
	return 0;
}

C题: 斐波那契

C题 提交链接

答案:6535086616739/3684083162760

记得开long long即可

代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int a[20];
void init()
{
	a[1]=1;
	a[2]=1;
	for(int i=3;i<=16;i++)
		a[i]=a[i-1]+a[i-2];
}
int main()
{
	init();
	ll fz=0,fm=1,g,q,cnt=0;
	for(int i=2;i<=14;i++)
	{
		g=a[i]*a[i-1];
		fz=fz*g+fm;
		fm*=g;
		q=__gcd(fz,fm);
		fz/=q;
		fm/=q;
	}
	printf("%lld/%lld\n",fz,fm);
	return 0;
}

D题: 数列重组(好题)

D题 提交链接

思路1:全排列(不会出现重复的序列)

  1. 全排列,数组内元素必须是有序的,因为全排列是按字典序递增逐一排列的
  2. 对每一种可能进行判断,判断有几处中断点:
    a. 0处:全增全减的情况,随便分为三段即可。
    b. 1处:已分2部分,二选其一再分一段即可。
    c. 2处:恰好3部分,还可以。
    d. 3处以上:就不行了。
  3. 怎么判断中断: 相邻元素进行比较
    ——开一个标记flag
    flag=0表示此刻不知道要递减还是递增
    flag=1表示此刻处于递增
    flag=-1表示次了处于递减
    ——判断是否矛盾,矛盾就是中断点
    ——相邻元素相等没有必要考虑。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[11]={-1100,2,3,3,3,5,6,6,7,7,8};
int main()
{
	int ans=0;
	do
	{
		int flag=0;
		int cnt=0;
		for(int i=2;i<=10;i++)
		{
			if(a[i]>a[i-1])   //递增
			{
				if(flag==-1)
				{
					cnt++;
					flag=0;
				}
				else
					flag=1;
			}
			else if(a[i]<a[i-1])
			{
				if(flag==1)
				{
					cnt++;
					flag=0;
				}
				else
					flag=-1;
			}
		}
		if(cnt<=2)
			ans++;
	}while(next_permutation(a+1,a+11));
	printf("%d\n",ans);
}

思路2:深搜(有产生重复的序列)

  1. 思路同上,如何去重
  2. 数组里面有3个3,2个6,2个7,最后结果除以24,因为24=3!* 2!* 2!。组合里面的定理,解释我也不会。

代码:

#include<stdio.h>
#include<string.h>
int b[14]={-1128,2,5,3,6,3,6,7,3,7,8};
int a[14];
int book[14];
int ans;
void dfs(int step)
{
	if(step==11)
	{
		int flag=0;
		int cnt=0;
		for(int i=2;i<=10;i++)
		{
			if(a[i]>a[i-1])//递增 
			{
				if(flag==-1)
				{
					cnt++;
					flag=0;
				}
				else
					flag=1;
			}
			if(a[i]<a[i-1])//递减 
			{
				if(flag==1)
				{
					cnt++;
					flag=0;
				}
				else
					flag=-1;
			}
		}
		if(cnt<=2)
			ans++;	
	}
	for(int i=1;i<=10;i++)
	{
		if(book[i]==0)
		{
			a[step]=b[i];
			book[i]=1;
			dfs(step+1);
			book[i]=0;
		}
	}
}
int main()
{
	ans=0;
	dfs(1);
	printf("%d\n",ans/24);
	return 0;
}

E题:三角形个数(好题)

E题 提交链接

答案:683228996

思路:找规律

  1. 正放三角和倒放三角分开来找
  2. 以正三角为例,可再分:边长为1,边长为2… 等等等,逐一去找,发掘规律。
  3. 会发现:每一类正三角个数都符合等差数列的前n项和公式:Sn=(1+n)*n/2 。
  4. 倒放三角也一样。
  5. 看下图解释:

在这里插入图片描述
在这里插入图片描述

下面附一张丑图供大家找三角

在这里插入图片描述

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
typedef long long ll;
const ll MOD=1e9+7;
int main()
{
	ll d=20210411,n;
	ll sumz=0;   //正三角个数
	for(n=d;n>=1;n--)
    	sumz=(sumz+n*(n+1)/2)%MOD;
	ll sumf=0;   //倒三角个数
	for(n=d-1;n>=1;n-=2)
    	sumf=(sumf+n*(n+1)/2)%MOD;
	printf("%lld\n",(sumz+sumf)%MOD);
	return 0;
}

F题:字符串(水题)

F题 提交链接

代码:

#include<stdio.h>
#include<string.h>
char a[330];
int main()
{
	int n,i,la,cnt=0;
	scanf("%d",&n);
	while(n--)
	{
		getchar();
		scanf("%[^\n]",a+1);
		la=strlen(a+1);
		for(i=1;i+3<=la;i++)
		{
			if(a[i]=='@'&&a[i+1]=='w'&&a[i+2]=='y'&&a[i+3]=='k')
			{
				cnt++;
				break;
			}
		}
	}
	printf("%d\n",cnt);
	return 0;
}

G题:最强对手矩阵

G题 提交链接

题意:
考场内实力总和最大的矩阵区域的实力和是多少

思路:三步优化的暴力

最暴力思维:

在这里插入图片描述

for(r1=1;r1<=n;r1++)
	for(r2=r1;r2<=n;r2++)
    	for(c1=1;c1<=m;c1++)
        	for(c2=c1;c2<=m;c2++)
        	{
            	sum=0;
            	for(i=r1;i<=r2;i++)
                	for(j=c1;j<=c2;j++)
                    	sum+=e[i][j];
            	if(maxx<sum)
                	maxx=sum;
        	}

显而易见,大大超时。

优化暴力第一步:利用前缀和思想

——内层循环求和,可以利用前缀和思想

  1. 把每一列的元素向后依次累加,取任意2行求和就很容易。
  2. 把每一行的元素向后依次累加,取任意2列求和就很容易。

——这里只利用1来做题,为什么不1和2一起用呢,本人感觉有点复杂,处理不清楚。

——利用1对原数组e操作后可得到的效果:

e[r2]-e[r1-1]:表示行[r1,r2]里的列元素加在了一起,
现可看作一维的数组(1行m列)。

暴力优化第二步:利用一维数组最大连续和的思想

——一维的非常简单的,忘记了请看该题该题

for(r1=1;r1<=n;r1++)
	for(r2=r1;r2<=n;r2++)
	{
    	sum=0;
    	for(c1=1;c1<=m;c1++)
    	{
        	sum+=(e[r2][c1]-a[r1-1][c1]);
        	if(maxx<sum)
            	maxx=sum;
        	if(sum<0)
            	sum=0;
    	}
	}

暴力优化第三步:矩阵转置

在这里插入图片描述
若改变n和m的值,需要进行矩阵转置。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int main()
{
	int n,m,r,c,flag=0;
	scanf("%d %d",&n,&m);
	if(n>m)
	{
		swap(n,m);
		flag=1;
	}
	int e[n+5][m+5];
	memset(e,0,sizeof(e));
	if(flag)          //进行转置 
	{
		for(r=1;r<=m;r++)
			for(c=1;c<=n;c++)
				scanf("%d",&e[c][r]); 
	}
	else
	{
		for(r=1;r<=n;r++)
			for(c=1;c<=m;c++)
				scanf("%d",&e[r][c]);
	}

	//把每一列的元素向后依次累加
	for(r=1;r<=n;r++)
		for(c=1;c<=m;c++)
			e[r][c]+=e[r-1][c];

	//前期准备已完成,利用最大连续字串和求解	
	int r1,r2,maxx=-INF,sum;
	for(r1=1;r1<=n;r1++)
		for(r2=r1;r2<=n;r2++)
		{
    		sum=0;
    		for(c=1;c<=m;c++)
    		{
        		sum+=(e[r2][c]-e[r1-1][c]);
        		if(maxx<sum)
            		maxx=sum;
        		if(sum<0)
            		sum=0;
    		}
		}
	printf("%d\n",maxx);	
	return 0;
} 

H:友谊纽带

H题 提交链接

思路:
——并查集,vector容器构建邻接表,广搜bfs

  1. 利用并查集判断是否连通(人与人是否认识)
  2. vector容器构建邻接表
  3. 对每一个人都用bfs,求出每个人的关系链长度,取最大。

代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int N=5500;
const int M=110000;
vector<int > vt[N];
int f[N],book[N];
int n,m,cnt;
struct Node{
	int x,s;
};
void init()
{
	for(int i=1;i<=n;i++)
    	f[i]=i;
}
int find(int x)
{
	if(x!=f[x])
    	f[x]=find(f[x]);
	return f[x];
}
void Merge(int u,int v)
{
	int tu=find(u);
	int tv=find(v);
	if(tu!=tv)
	{
    	f[tv]=tu;
    	cnt++;
	}
}
int bfs(int x)
{
	queue<Node> q;
	struct Node u,v;
	memset(book,0,sizeof(book));
	u.x=x;
	u.s=0;
	q.push(u);
	int i,s,tx,ts,maxs=0;
	while(!q.empty())
	{
    	u=q.front();
    	x=u.x;
    	s=u.s;
    	q.pop();
    	for(i=0;i<vt[x].size();i++)
    	{
        	tx=vt[x][i];
        	ts=s+1;
        	if(!book[tx])
        	{
            	book[tx]=1;
            	v.x=tx;
            	v.s=ts;
            	maxs=ts;//记录最大值
            	q.push(v);
        	}
    	}
	}
	return maxs;
}
int main()
{
	scanf("%d %d",&n,&m);
	cnt=0;
	init();
	int a,b,i;
	while(m--)
	{
    	scanf("%d %d",&a,&b);
    	Merge(a,b);
    	vt[a].push_back(b);
    	vt[b].push_back(a);
	}
	if(cnt!=n-1)
    	printf("-1\n");
	else
	{
    	int maxx=0;
    	for(i=1;i<=n;i++)
        	maxx=max(maxx,bfs(i));
    	printf("%d\n",maxx);
	}
	return 0;
}

I: 传送门

I题 提交链接

思路:Kruskal算法模板

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=5500;
const int M=110000;
struct Node{
	int a,b,t;
}q[M];
int f[N];
int n,m,count1;
bool cmp(Node a,Node b)
{
	return a.t<b.t;
}
void init()
{
	for(int i=0;i<=n;i++)
    	f[i]=i;
}
int find(int x)
{
	if(x!=f[x])
    	f[x]=find(f[x]);
	return f[x];
}
void Merge(int u,int v)
{
	int tu=find(u);
	int tv=find(v);
	if(tu!=tv)
	{
    	count1++;
    	f[tv]=tu;
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
    	scanf("%d %d %d",&q[i].a,&q[i].b,&q[i].t);
	sort(q+1,q+1+m,cmp);
	init();
	count1=0;
	for(int i=1;i<=m;i++)
	{
    	Merge(q[i].a,q[i].b);
    	if(count1==n-1)
    	{
        	printf("%d\n",q[i].t);
        	break;
    	}
	}
	if(count1!=n-1)
    	printf("-1\n");
	return 0;
}

J:不会

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值