试题A:平方序列
题解
签到!
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define N 9999
int main()
{
long long int i,j;
for(i=2020;i<=N;i++)
{
long long int x=i*i-2019*2019;
long long int jj=i*i+x;
j=sqrt(jj);
if(j*j==jj)
{
printf("%d",i+j);
return 0;
}
}
return 0;
}
答案:7020
试题B:质数拆开
题解
(01背包变形:我过去写的01背包以及一道uva的01背包题:https://blog.csdn.net/m0_52103105/article/details/116330899)
状态转移方程:dp(i,j)+=dp(i-1,j-perm【i】);
i代表第i个素数,j代表0~2019的数,我们要求第i个素数时,j能有多少种组成方法,直到最后的dp【素数数目】【2019】就能得到最终答案
代码中省略了对第一个素数的状态转移,但我们很简单的推出第一个素数只能组成2。
注意:dp【i】【0】也要为1,因为题目说明了是若干个也就是1个素数也可以算作组成的一种。
注意:这题不能用线性动态,线性动态是要在不同顺序算不同情况下使用的,这道题是不分顺序的(而且存在标记的情况下线性动态不能使用记忆化)
线性动态求不同顺序:
#include <stdio.h>
#include <stdlib.h>
#define N 2019
int perm[N];
int flag[N+1];
int count=0;
int vis[2020];
long long int d[2020];
long long int dp(int S)
{
int i;
if(S==0)
return 1;
if(S<0)
return 0;
/*if(d[S]!=-1)
return d[S];
*/
d[S]=0;
for(i=0;i<count;i++)
{
if(vis[i]==1)
continue;
vis[i]=1;
d[S]+=dp(S-perm[i]);
vis[i]=0;
}
return d[S];
}
int main()
{
//普通筛选法求素数,素数的倍数不是素数
int i,j;
for(i=2;i<=N;i++)
{
if(!flag[i])
{
perm[count++]=i;
for(j=i*2;j<=N;j+=i)
flag[j]=1;
}
}
memset(d,-1,sizeof(d));
printf("%lld",dp(2019));
return 0;
}
正确答案:
#include <stdio.h>
#include <stdlib.h>
#define N 2019
int perm[N];
int flag[N+1];
int count=0;
long long int dp[400][2020]={
0};
int isPerm(int x)
{
for(int i=0;i<count;i++)
if(x==perm[i])
return 1;
return 0;
}
int main()
{
//普通筛选法求素数,素数的倍数不是素数
int i,j;
for(i=2;i<=N;i++)
{
if(!flag[i])
{
perm[count++]=i;
for(j=i*2;j<=N;j+=i)
flag[j]=1;
}
}
/*printf("count%d\n",count);
for(i=0;i<count;i++)
printf("%d\n",perm[i]);*/
dp[0][2]=dp[0][0]=1;
for(i=1;i<count;i++)
for(j=0;j<=2019;j++)
{
dp[i][j]=dp[i-1][j]; //要继承上一个素数所原有的组合数
if(j>=perm[i])
dp[i][j]+=dp[i-1][j-perm[i]];
}
printf("%lld",dp[count-1][2019]);
return 0;
}
状态压缩:
注意:要自顶向下开始,自底向上开始会改变前面的数导致后面的数重复运算而过大
#include <stdio.h>
#include <stdlib.h>
int count=0;
int flag[2020];
int perm[390];
int main()
{
int i,j;
for(i=2;i<=2019;i++)
{
if(!flag[i])
{
perm[count++]=i;
for(j=i*2;j<=2019;j+=i)
flag[j]=1;
}
}
long long int dp[2020]={
1,0};
for(i=0;i<count;i++)
for(j=2019;j>=perm[i];j--) //要自顶向下开始因为自底向上开始会改变前面的数导致后面的数重复运算
dp[j]+=dp[j-perm[i]];
printf("%lld",dp[2019]);
return 0;
}
答案:55965365465060
试题C:拼接
题解
要让右边图形翻转也能与左边的图形重合,也就是说我们翻转后右边的图形并没有改变,那么如何让图形不改变:就是轴对称图形!(利用由对称轴分割出来的两边图形相等)
还要注意一点:题目说的是一根木根我们切割的只是三个三角形中中间的一个,
比如说:
我们由此进一步确立了右边图形的对称轴该是此7x7正方形的对称轴(注意对称轴有两条),不然就会因为右边正方形的限制导致虽然图形没有改变但是无法拼接。
由于我们只能翻转右边所以我们只需要考虑一条对称轴
了解了最难的原理:那么我们很简单就能想出代码,就是dfs,从(0,0)有多少种到对称轴的办法。
#include <stdio.h>
#include <stdlib.h>
int vis[8][8];
int ans;
int dir[4][2]={
{
1,0},{
0,1},{
-1,0},{
0,-1}};
void DFS(int x,int y)
{
if(x+y==7)
{
ans++;
return;
}
for(int i=0;i<4;i++)
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(xx>=0 && xx<=7 && yy>=0 && yy<=7 && !vis[xx][yy] && xx+yy<=7) //注意图片把正方形分成七份有八个顶点不注意的话答案就错了
{
vis[xx][yy]=1;
DFS(xx,yy);
vis[xx][yy]=0;
}
}
}
int main()
{
vis[0][0]=1;
DFS(0,0);
printf("%d",ans-2); //答案还要减2去掉一直向右和一直向下的情况
return 0;
}
答案:45406
试题D:求值
题解
送分题
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j;
for(i=100;;i++)
{
int count=0;
for(j=1;j<=i;j++)
if(i%j==0)
count++;
if(count==100)
{
printf("%d",i);
return 0;
}
}
return 0;
}
答案:45360
试题E
有一个7X7的方格。方格左上角顶点坐标为(0,0),右下角坐标为(7,7)。
求满足下列条件的路径条数:
1、起点和终点都是(0,0)
2、路径不自交
3、路径长度不大于12
4、对于每一个顶点,有上下左右四个方向可以走,但是不能越界。
例如,图中路线,左上角顶点(0,0),路线长度为10
题解
dfs送分题了,但要注意按照我的写法,答案要减2。
因为题目要求不能自交,我(0,0)是没有标记的,所以有两个ans是往右走后直接往左走回来和往下走后直接往上走回来。
#include <stdio.h>
#include <stdlib.h>
//又是分成七份八个格子别想让我再上当了
int vis[8][8];
int ans=0;
int dir[4][2]={
{
1,0},{
-1,0},{
0,1},{
0,-1}};
int flag=0;
void DFS(int x,int y,int step)
{
if(step>12)
return;
if(x==0 && y==0)
{
flag++;
if(flag==2 && step<=12)
{
flag=1;
ans++;
return;
}
}
for(int i=0;i<4;i++)
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(xx>=0 && xx<=7 && yy>=0 && yy<=7 && !vis[xx][yy])
{
vis[xx][yy]=1;
DFS(xx,yy,step+1);
vis[xx][yy]=0;
}
}
}
int main()
{
DFS(0,0,0);
printf("%d",ans-2);
/*答案要减2,因为题目要求不能自交,我(0,0)是没有标记的,
所以有两个ans是往右走后直接往左走回来和往下走后直接往上走回来*/
return 0;
}
答案:206
转载:https://blog.csdn.net/m0_52103105/article/details/117391882