A卡片
简单的% 和/
B直线
答案 40257
思路:
1、直线两点式方程:(y-y1)/(y1-y2)=(x-x1)/(x1-x2)
整理的AX+BY+C=0
其中:
A=y1-y2
B=x2-x1
C=x1y2-y1x2
记得A B C要化简,即同时除以他们的最大公约数,方便去重。
只要A B C中有一个不同,就是不同的直线
2、去重:
遍历直线LINE结构体数组,如果两个结构体中A,B,C数据相同,那么就认为这两个直线是重合的直线。第一条直线的不能判断它与哪条直线重合,那么把该直线的value数据赋值为1。从第二条直线开始,与它前面的所有直线作比较,如果前面有与该直线重合的直线,那么该直线的value数据不变,等于前一个直线的value数值;如果前面没有与该直线重合的直线,那么该直线的value数据将在前一个直线的value数值加1。遍历所有的直线后,最后一条直线的value值就是去重后的直线条数。
注意:最终的直线条数是 最后一条直线的value值 加上 平行于x轴(包含x轴)直线和平行于y轴(包含y轴)直线和。
#include <bits/stdc++.h>
#define row 21
#define col 20
#define pointnum 420
using namespace std;
const int maxn=1000000;
struct POINT{//存储所有点的坐标
int x;
int y;
}point[row*col];
struct LINE{//存储所有直线的数据
int a;
int b;
int c;
int value;
}line[maxn];
int sum=0;//直线的数量
int gcd(int x,int y){//求两个数(可能是0 或者 负数)绝对值的 最大公约数
if(x==0) return y;//0的情况
if(y==0) return x;
if(x<0) x=0-x;//负数的情况
if(y<0) y=0-y;
if(x%y==0) return y;//正常情况
else if(y%x==0) return x;
else if(x>y) return gcd(x-y,y);
else return gcd(y-x,x);
}
int beingline(int x1,int y1,int x2,int y2){//构造直线ABC并且存储value
//任意两点构成一条直线
int a,b,c;
if(x1==x2) return 0;//除去竖直线
if(y1==y2) return 0;//除去水平线
if(x1!=x2&&y1!=y2){//其他斜线
a=y1-y2;
b=x2-x1;
c=x1*y2-y1*x2;
}
int fm;//系数ABC 的最大公约数
if(c==0) fm=gcd(a,b);
else fm=gcd(gcd(a,b),c);
line[sum].a=a/fm;
line[sum].b=b/fm;
line[sum].c=c/fm;
line[sum].value=1;
sum++;//直线总数
return 0;
}
int main(){
int k=0;
//存入点的数据
for(int i=0;i<col;i++){
for(int j=0;j<row;j++){
point[k].x=i;
point[k].y=j;
k++;
}
}
//存入直线数据
for(int i=0;i<pointnum;i++){
for(int j=i+1;j<pointnum;j++){
beingline(point[i].x,point[i].y,point[j].x,point[j].y);
}
}
//去重过程第i条直线跟0~i-1条直线比 看是否是同一条直线
//value 记录当前直线之前不同直线的个数即最后一条直线的value+col+row是答案
for(int i=1;i<sum;i++){
for(int j=0;j<i;j++){
if(line[i].a==line[j].a&&line[i].b==line[j].b&&line[i].c==line[j].c){
line[i].value=line[i-1].value;
break;
}
line[i].value=line[i-1].value+1;
}
}
printf("%d",line[sum-1].value + col + row);//加上水平线和竖直线
return 0;
}
C 货物摆放
问题描述
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的长方体。即在长、宽、高的方向上分别堆 L、W、H 的货物,满足 n = L ×W×H。
给定 n,请问有多少种堆放货物的方案满足要求。
例如,当 n=4 时,有以下 6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×1。
请问,当 n = 2021041820210418 (注意有 16 位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
输出:
2430
最开始打算,这种暴力复杂度高,电脑运行不出来。
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
long long ans=0;
for(int i=1;i<=2021041820210418;i++){
for(int j=1;j<=2021041820210418;j++){
for(int k=1;k<=2021041820210418;k++){
if(i*j*k==2021041820210418)
ans++;
}
}
}
cout<<ans<<endl;
return 0;
}
化繁从简是这道题的奥义,降低循环次数(先找n的所有因子,再从因子里面循环)
D路径
【问题描述】
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
答案
10266837
思路:
解题思路:
首先确定问题:求结点1到结点2021之间的最短路径,又因为知道只要结点相差大于21,则无法有边到达,换句话说,结点值相差大于21则需要借助一个甚至多个结点到达,若结点相差小于等于21,则可以确定最短路径。所以我们可以每个结点轮换遍历求最小路径。比如知道了结点1到结点i的最短路径,可以通过结点i求出结点i到结点(i+21)的所有的最短路径(但不是最后的结果,很可能在结点i+j(1<j<21)的轮换遍历时会更新最短路径
确定状态:
定义状态:f[i]为结点1到结点i的最短路径,初始值为0,代表这个结点1到结点i的最短路径还未更新。
边界值:即结点1到结点1的最短路径为1 or 结点1到结点2的最短路径为2(可直接赋值)
f[j]=0,代表这个最短路径还未被更新,知道结点i之后,最多可以更新到结点i+21的值,如果到i+22之后则他们无边相连,无法直接更新。
如果f[j]≠0,说明已经更新过f[j]的值了,则继续更新f[j]的值,则需要与之前最后一次更新f[j]的值最比较,取较小着。
代码如下:
#include<iostream>
using namespace std;
int f[2050];//f[i]表示结点1到 结点i的最短路径值
int lcm(int a,int b){//最小公倍数
int t;
int x=a;
int y=b;
while(y!=0){
t=x%y;
x=y;
y=t;
}
return a*b/x;
// int maxbound=a*b;//这种方法超时
// for(int i=a;i<=maxbound;i++){
// if(i%a==0&&i%b==0){
// return i;
// }
// }
}
int main(){
for(int i=0;i<2050;i++) f[i]=0;
for(int i=1;i<=2021;i++){
for(int j=i+1;j<=i+21;j++){
if(f[j]==0) f[j]=f[i]+lcm(j,i);
else f[j]=min(f[j],f[i]+lcm(j,i));
}
}
cout<<f[2021]<<endl;
return 0;
}
E回路计数
问题描述 蓝桥学院由 21 2121 栋教学楼组成,教学楼编号 1 11 到 21 2121。对于两栋教学楼 a aa 和 b bb,当 a aa 和 b bb 互质时,a aa 和 b bb 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。
小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案?两个访问方案不同是指存在某个
i ii,小蓝在两个访问方法中访问完教学楼 i ii 后访问了不同的教学楼。 提示:建议使用计算机编程解决问题。
答案提交 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案: 881012367360
思路:状态压缩+DP
代码:
#include<iostream>
using namespace std;
typedef long long ll;
//求 a,b最大公约数,互质数---两个数的最大公约数是1
int gcd(int a, int b)//6 4
{
if(a%b==0) return b;
else if(b%a==0) return a;
else if(a>b) return gcd(a-b, b);
else return gcd(b-a, a);
}
const int n = 21, m = 1 << n;
ll dp[m][n];//dp[i][j]:走到j点时,状态为i时的方案总数
bool e[n][n];//标记两点是否互质
int main()
{
for (int i=1; i<=21;i++)
for (int j = 1; j <= 21; j ++)
if(gcd(i,j)==1)
e[i - 1][j - 1] = true;//互质为true,且把21个点标号为0~20
dp[1][0] = 1;//初始化:走到第一个点是状态为0001的方案数为1;
for (int i = 1; i <= m - 1; i ++) {
//m=1<<21即22个1的二进制,m-1是21个1的二进制
for (int j = 0; j <= 20; j ++){
if(i >> j & 1)//j这栋楼我是不是访问过,访问过才向下走;
for (int k = 0; k <= 20; k ++){
if(((i>>k)&1)&&e[k][j])
//状态转移方程:走到j栋楼状态为i的方案总数=
//走到j之前的那个状态(i-(1<<j)),对应的最后楼栋是任意一栋满足条件的,k循环;
//条件是i这个状态已访问过k--(i>>k)&1==1;并且k和j互质,即k能走到j.
dp[i][j]+= dp[i-(1<<j)][k];
}
}
}
ll ans = 0;
for (int i=1;i<=20;i++)//楼栋数0~20 ,回到第一栋楼前的最后一栋楼可以是标号1~20的任意一
ans+=dp[m-1][i];//状态m-1是21个1的二进制,i是除了第一栋 所有楼栋循环
cout<<ans<<endl;
return 0;
}