A题:6的个数
答案:602
代码:略
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题: 斐波那契
答案: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题: 数列重组(好题)
思路1:全排列(不会出现重复的序列)
- 全排列,数组内元素必须是有序的,因为全排列是按字典序递增逐一排列的
- 对每一种可能进行判断,判断有几处中断点:
a. 0处:全增全减的情况,随便分为三段即可。
b. 1处:已分2部分,二选其一再分一段即可。
c. 2处:恰好3部分,还可以。
d. 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:深搜(有产生重复的序列)
- 思路同上,如何去重
- 数组里面有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题:三角形个数(好题)
答案:683228996
思路:找规律
- 正放三角和倒放三角分开来找
- 以正三角为例,可再分:边长为1,边长为2… 等等等,逐一去找,发掘规律。
- 会发现:每一类正三角个数都符合等差数列的前n项和公式:Sn=(1+n)*n/2 。
- 倒放三角也一样。
- 看下图解释:
下面附一张丑图供大家找三角
代码:
#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题:字符串(水题)
代码:
#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题:最强对手矩阵
题意:
考场内实力总和最大的矩阵区域的实力和是多少
思路:三步优化的暴力
最暴力思维:
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;
}
显而易见,大大超时。
优化暴力第一步:利用前缀和思想
——内层循环求和,可以利用前缀和思想
- 把每一列的元素向后依次累加,取任意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:友谊纽带
思路:
——并查集,vector容器构建邻接表,广搜bfs
- 利用并查集判断是否连通(人与人是否认识)
- vector容器构建邻接表
- 对每一个人都用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: 传送门
思路: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;
}