1. 知识点总结
卡在了第一题……然后没有⏲
还好不是考试……不然心态可以说炸得没边了😱
但是其他的题目比较基础,Dijkstra需要好好复习,好久没做过了
题号 | 难度 | 知识点 |
---|---|---|
1160 | 🐸🐸 | 数学+找规律+搜索DFS+剪枝(个人觉得需要三刷) |
1161 | 🐸 | 链表 |
1162 | 🐸 | 二叉树遍历 |
1163 | 🐸 | Dijkstra |
2. 分题题解
2.1 第一题
比较搞心态的一题,一开始注意到了末尾的数字只可以是9,不然
n=m+1
是无法导出n与m公因数大于2
的,但是第一遍代码完全没有涉及这个……因为以为不是很大的点……
第一次:2/20
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
int m;
int k;
//素数判断
bool isPrime(ll x){
if(x<=2)return false;
if(x==3||x==5||x==7)return true;
for(ll i=2;i*i<=x;i++){
if(x%i==0)return false;
}
return true;
}
//最大公因数:辗转相除法 a>b
ll maxDiv(ll a,ll b){
if(a<b){
swap(a,b);
}
ll r=a;
while(r!=0){
r=a%b;
a=b;
b=r;
}
return a;
}
struct Ans{
int n;
ll num;
};
//计算整数每位相加之和
int CountDigits(string now){
int len=now.size();
int sum=0;
for(int i=0;i<len;i++){
sum+=now[i]-'0';
}
return sum;
}
//容许的最小值
int lowest=0;
void CountLowest(int digit_num,int sum,int& lowest){
if(digit_num==1){
lowest=max(lowest,sum);
}
else{
digit_num--;
int temp=sum-9*digit_num;
lowest=max(lowest,temp);
}
//printf("%d个数字 ,和为%d,最小值%d\n",digit_num+1,sum,lowest);
}
//深搜:搜索所有符合条件的解
vector<Ans>ans;
void dfs(int num,int sum,string now,int digit_num,int mylow){
//printf("digit_num=%d now=%s\n",digit_num,now.c_str());
//当前遍历到的数字是num,当前和为sum,now存储目前的所有数字
now=now+to_string(num);
sum+=num;
digit_num++;
if(sum>m||digit_num>k)return;
//知道接下来不够了
if(sum+9*(k-digit_num)<m)return;
if(sum==m&&digit_num==k){
//1 0 && 11 10不可能有大于2的公因数
ll candidate=stol(now);
string candidate1=to_string(candidate+1);
int dsum=CountDigits(candidate1);
//printf("&&&&&&&&&&&&&&&&&&&dsum=%d m=%d\n",dsum,m);
if(isPrime(maxDiv(dsum,m))){
Ans temp;
temp.n=dsum;
temp.num=candidate;
ans.push_back(temp);
}
return;
}
//继续探索
CountLowest(k-digit_num,m-sum,mylow);
for (int i=lowest;i<10;i++){
dfs(i,sum,now,digit_num,mylow);
}
return;
}
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++){
scanf("%d%d",&k,&m);
//找到k个数字组成的整数A,它的和是m
//n=(A+1)和
//m和n的最大公因数为大于2的素数
CountLowest(k,m,lowest);
for(int i=lowest;i<10;i++){
if(i==0)continue;
dfs(i,0,"",0,0);
}
printf("Case %d\n",i);
int len=ans.size();
if(len==0){
printf("No Solution\n");
}
for(int i=0;i<len;i++){
printf("%d %lld\n",ans[i].n,ans[i].num);
}
ans.clear();
}
return 0;
}
第二次:订正
第一版:10分
#include<bits/stdc++.h>
using namespace std;
//有i个9,m-n=8+9*(i-1)
int n;
int k,m;
typedef long long ll;
bool Prime(ll x){
if(x<=2)return false;
for(ll i=2;i*i<=x;i++){
if(x%i==0)return false;
}
return true;
}
ll gcd(ll a,ll b){
if(a<b)swap(a,b);
ll r=b;
while(r!=0){
r=a%b;
a=b;
b=r;
}
return a;
}
struct Ans{
ll num;
int n;
};
vector<Ans>ans;
int target_sum;
int digit_sum;
string nine;
void dfs(int pos,int num,int sum,string now){
//cout<<"now "<<now<<endl;
pos++;
sum+=num;
now+=to_string(num);
if(sum==target_sum&&pos==digit_sum){
//ans.push_back(stol(now+nine));
Ans temp;
temp.n=n;
temp.num=stol(now+nine);
ans.push_back(temp);
return;
}
if(sum>target_sum||pos>digit_sum){
return;
}
for(int i=0;i<=min(9,target_sum-sum);i++){
dfs(pos,i,sum,now);
}
return ;
}
int N;
bool cmp(Ans a,Ans b){
if(a.n!=b.n){
return a.n<b.n;
}else{
return a.num<b.num;
}
}
int main(){
scanf("%d",&N);
for(int l=1;l<=N;l++){
printf("Case %d\n",l);
scanf("%d%d",&k,&m);
bool flag=false;
for(int num9=1;num9<=k;num9++){
//末尾num9的个数
n=m-(8+9*(num9-1));
if(n<=1){
break;
}
int div=gcd(m,n);
if(Prime(div)){
//合法
target_sum=m-num9*9;
digit_sum=k-num9;
nine="";
for(int c=0;c<num9;c++){
nine+="9";
}
for(int i=1;i<=min(9,target_sum);i++){
dfs(0,i,0,"");
}
}
}
//输出答案
int len=ans.size();
if(len!=0){
flag=true;
}
sort(ans.begin(),ans.end(),cmp);
for(int c=0;c<len;c++){
printf("%d %lld\n",ans[c].n,ans[c].num);
}
ans.clear();
if(!flag){
printf("No Solution\n");
}
}
return 0;
}
第二版:AC
#include<bits/stdc++.h>
using namespace std;
struct Ans{
int n;
int A;
};
int N;
int k,m;
int n;
bool isPrime(int x){
if(x<=2)return false;
if(x==2||x==3||x==5||x==7)return true;
for(int i=2;i*i<=x;i++){
if(x%i==0)return false;
}
return true;
}
int gcd(int a ,int b){
//a>b
return b==0?a:gcd(b,a%b);
}
int digitSum(int x){
int sum=0;
while(x){
sum+=(x%10);
x/=10;
}
return sum;
}
vector<Ans>ans;
void dfs(int A,int sum,int rest_k){
if(rest_k==0){
if(sum==m){
int n=digitSum(A+1);
if(isPrime(gcd(m,n))){
ans.push_back({n,A});
}
}
}else if(rest_k>0){
for(int i=0;i<=9;i++){
//剪枝
if(sum+i<=m&&sum+i+(rest_k-1)*9>=m){
dfs(A*10+i,sum+i,rest_k-1);
}
}
}
}
bool cmp(Ans a,Ans b){
if(a.n!=b.n){
return a.n<b.n;
}else{
return a.A<b.A;
}
}
int main(){
scanf("%d",&N);
for(int times=1;times<=N;times++){
scanf("%d%d",&k,&m);
printf("Case %d\n",times);
//dfs搜索答案
for(int i=1;i<=9;i++){
dfs(i,i,k-1);
}
sort(ans.begin(),ans.end(),cmp);
//输出答案
int len=ans.size();
for(int i=0;i<len;i++){
printf("%d %d\n",ans[i].n,ans[i].A);
}
ans.clear();
if(len==0){
printf("No Solution\n");
}
}
return 0;
}
2.2 第二题
链表的基础题(混合STL),注意输出格式以及-1的位置
#include<bits/stdc++.h>
using namespace std;
//链表操作
struct Node{
int addr;
int val;
int next;
};
int head1;
int head2;
int n;
int addr,val,next_addr;
map<int,Node>nodes;
vector<Node>v1;
vector<Node>v2;
void Change(vector<Node>v1,vector<Node>v2){
int lena=v1.size();
int lenb=v2.size();
int j=lenb-1;
for(int i=0;i<lena;i++){
if(i!=lena-1)
printf("%05d %d %05d\n",v1[i].addr,v1[i].val,v1[i+1].addr);
else
printf("%05d %d -1\n",v1[i].addr,v1[i].val);
if(j>=0){
i++;
//打印
if(1)
printf("%05d %d %05d\n",v1[i].addr,v1[i].val,v2[j].addr);
else
printf("%05d %d -1\n",v1[i].addr,v1[i].val);
//打印
if(i+1!=lena)
printf("%05d %d %05d\n",v2[j].addr,v2[j].val,v1[i+1].addr);
else
printf("%05d %d -1\n",v2[j].addr,v2[j].val);
j--;
}
}
}
int main(){
scanf("%d%d%d",&head1,&head2,&n);
for(int i=0;i<n;i++){
scanf("%d%d%d",&addr,&val,&next_addr);
Node temp;
temp.addr=addr;
temp.val=val;
temp.next=next_addr;
nodes[addr]=temp;
}
int p=head1;
//遍历v1
while(p!=-1){
Node temp=nodes[p];
v1.push_back(temp);
p=temp.next;
}
//遍历v2
p=head2;
while(p!=-1){
Node temp=nodes[p];
v2.push_back(temp);
p=temp.next;
}
int len1=v1.size();
int len2=v2.size();
if(len1>=2*len2){
Change(v1,v2);//大 小
}else {
Change(v2,v1);
}
return 0;
}
2.3 第三题
这一题考察静态二叉树的遍历,需要注意的是:
- 在正常的后续遍历中需要判断是否出现某一结点是“+”、“-”且没有左孩子的情况(也就是此时非运算符,而是符号表示),此时后序遍历改成前序遍历
- root的查找主要是看输入的时候,哪一个结点没有作过孩子
#include<bits/stdc++.h>
using namespace std;
//静态二叉树
struct Node{
char data[15];
int left;
int right;
};
int n;
vector<Node>v;
void postTravel(int root){
if(root==-1){
return;
}
if((v[root].data[0]=='-'||v[root].data[0]=='+')&&strlen(v[root].data)==1&&v[root].left==-1){
printf("(");
printf("%s",v[root].data);
postTravel(v[root].left);
postTravel(v[root].right);
printf(")");
}else{
printf("(");
postTravel(v[root].left);
postTravel(v[root].right);
printf("%s",v[root].data);
printf(")");
}
}
int root;
bool isNotRoot[50]={false};
int main(){
scanf("%d",&n);
v.resize(n+1);
for(int i=1;i<=n;i++){
scanf("%s%d%d",v[i].data,&v[i].left,&v[i].right);
if(v[i].left!=-1){
isNotRoot[v[i].left]=true;
}
if(v[i].right!=-1){
isNotRoot[v[i].right]=true;
}
}
for(int i=1;i<=n;i++){
if(!isNotRoot[i]){
root=i;
break;
}
}
postTravel(root);
return 0;
}
2.4 第四题
本想要不要深搜将每一个(st,ed)对应得所有可能性输出,和q中询问得序列比较,不过后来直接在Dij函数中将u的初始化引入q,在算法过程中直接比较,时间空间都可以节省😜
#include<bits/stdc++.h>
using namespace std;
const int INF=99999999;
//检查一个序列是否是dijkstra序列
int n,m,k;
const int maxn=1009;
int graph[maxn][maxn];
int u,v,d;
vector<int>q;
vector<int>temp;
bool Dij(int st,int ed){
//st到ed的最短距离
temp.clear();
vector<int>dis(n+1);//起点到各个顶点的最短距离
vector<bool>vis(n+1);
fill(vis.begin(),vis.end(),false);
fill(dis.begin(),dis.end(),INF);
dis[st]=0;//起点自身的距离为0
int id=0;
for(int i=1;i<=n;i++){
//int u=-1,min_dis=INF;
int u=q[id];
int min_dis=dis[u];
id++;
for(int j=0;j<=n;j++){
if(vis[j]==false&&dis[j]<min_dis){
return false;
}
}
if(u==-1){
break;
}
temp.push_back(u);
vis[u]=true;
for(int v=1;v<=n;v++){
if(!vis[v]&&graph[u][v]!=-1&&dis[v]>graph[u][v]+dis[u]){
dis[v]=graph[u][v]+dis[u];
}
}
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
memset(graph,-1,sizeof(graph));
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&d);
graph[u][v]=d;
graph[v][u]=d;
}
scanf("%d",&k);
q.resize(n);
for(int i=0;i<k;i++){
//询问序列
for(int j=0;j<n;j++){
scanf("%d",&q[j]);
}
//处理
int st=q[0];
int ed=q[n-1];
if(Dij(st,ed)){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}
3. 参考链接
- 1160博客思路讲解
- 1160博客题解(比较简单的一版DFS)