第一题:dfs+剪枝
k位数不能所有的数字都遍历一遍,必须有选择的。选择k位数和为m的。
remain为剩下未分配数的位置的和。remain必须小于等于剩下的位数*9。因为若是大于的话,剩下的所有位数都为9,都满足不了所有位加起来为m。比如k=3,m=20,如果第一位为1,则return。因为无论后面两位是什么,这个三位数的和都小于20。所以
这样剪枝。就是在位数为k,所有位和为m的数里找了。
cnt为当前已经分配了数字的位数。remain为剩下的位数的数字之和。gdb为辗转相除法。当时差点没想起来,真的没印象了,名字都记错。判断质数,等于2也返回false,是因为要求最大公约数为大于2的质数。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
struct node{
LL val;
int n;
friend bool operator <(node a,node b)
{
if(a.n!=b.n)
{
return a.n<b.n;
}else
{
return a.val<b.val;
}
}
};
int N,k,m;
set<node> ans;
bool isprime(int x)
{
if(x<=2) return false;
for(int i=2;i*i<=x;i++)
{
if(x%i==0) return false;
}
return true;
}
int gdb(int a,int b)
{
if(b==0) return a;
else{
a = a%b;
return gdb(b,a);
}
}
int sum(LL x)
{
int sum = 0;
while(x!=0)
{
sum+=x%10;
x/=10;
}
return sum;
}
void dfs(int cnt,LL num,int remain)
{
if(remain>(k-cnt)*9) return;
if(cnt==k)
{
LL temp = num + 1;
int n = sum(temp);
int x = gdb(max(m,n),min(m,n));
if(isprime(x))
{
ans.insert(node{num,n});
}
return;
}
for(int i=0;i<=min(remain,9);i++)
{
if(cnt==0&&i==0) continue;
dfs(cnt+1,num*10+i,remain-i);
}
}
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
ans.clear();
scanf("%d%d",&k,&m);
dfs(0,0,m);
printf("Case %d\n",i);
if(ans.size()==0)
{
printf("No Solution\n");
}else
{
for(auto it:ans)
{
printf("%d %ld\n",it.n,it.val);
}
}
}
return 0;
}
结果:
第二题:
经典的链与链之间的操作。用unordered_map存初始数据。再从第一个结点开始遍历。把两个链存进v1,v2。
再遍历v1,v2。把它按照规则存进ans。再输出ans。注意最后一个结点。以及地址的位数。要注意,是谁长谁是v1.而不是第一个链。
#include<bits/stdc++.h>
using namespace std;
struct node{
int add;
int val;
int next1;
};
int root1,root2,m;
unordered_map<int,node> hash1;
vector<node> v1,v2,ans,temp;
int main()
{
scanf("%d%d%d",&root1,&root2,&m);
for(int i=0;i<m;i++)
{
int add,val,next1;
scanf("%d%d%d",&add,&val,&next1);
hash1[add]=node{add,val,next1};
}
int root = root2;
while(root!=-1)
{
v1.push_back(hash1[root]);
root = hash1[root].next1;
}
root = root1;
while(root!=-1)
{
v2.push_back(hash1[root]);
root = hash1[root].next1;
}
if(2*v1.size()<=v2.size())
{
temp = v1;
v1= v2;
v2 = temp;
}
int cnt1 = 0,cnt2 = v2.size()-1;
while(cnt1<v1.size()&&cnt2>=0)
{
ans.push_back(v1[cnt1++]);
if(cnt1>=v1.size())break;
ans.push_back(v1[cnt1++]);
if(cnt1>=v1.size())break;
ans.push_back(v2[cnt2--]);
if(cnt2<0)break;
}
while(cnt1<v1.size())
{
ans.push_back(v1[cnt1++]);
}
while(cnt2>=0)
{
ans.push_back(v2[cnt2--]);
}
for(int i=0;i<ans.size();i++)
{
if(i!=ans.size()-1)
printf("%05d %d %05d\n",ans[i].add,ans[i].val,ans[i+1].add);
else
{
printf("%05d %d -1\n",ans[i].add,ans[i].val);
}
}
return 0;
}
结果:
第三题:
给出每个结点的两个孩子与内容。输出后缀表达式。首先,把所有的孩子,除了-1外都存下来。缺的哪一个就是根节点。
再遍历树。注意,若结点,左右子树都有,则后序,若无左子树,则先序遍历。把结果存进string,再输出。
#include<bits/stdc++.h>
using namespace std;
struct node{
string s;
int left;
int right;
};
vector<node> v;
unordered_map<int,bool> hash1;
int N;
string ans;
string dfs(int now)
{
if(now==-1) return "";
string str;
if(v[now].right!=-1&&v[now].left!=-1)
str = "(" + dfs(v[now].left) + dfs(v[now].right) + v[now].s + ")";
else if(v[now].left==-1)
{
str = "(" + v[now].s + dfs(v[now].right) + ")";
}
return str;
}
int main()
{
scanf("%d",&N);
v.resize(N+1);
for(int i=1;i<=N;i++)
{
cin>>v[i].s>>v[i].left>>v[i].right;
if(v[i].left!=-1)
hash1[v[i].left]=true;
if(v[i].right!=-1)
hash1[v[i].right]=true;
}
int u = 1;
while(hash1[u]==true&&u<=N) u++;
ans = dfs(u);
cout<<ans;
return 0;
}
结果:
第四题
dijkstra过程为每一轮找到与原点最近的结点加入以经确定路径集合。而这找最近点的顺序可以形成序列。考察序列是否为dij序列。每一轮,看序列上对应的点,是否是这一步距离源点最近的点,若不是,则返回false。若是,则把该点加入已确定的点,再通过相应的边更新其余点距离源点的距离。直到所有点都确定了最短路径。则返回true。
刚开始,我以为这题应该会很容易超时。所以准备用优先队列,查找距离源点最近点会快一点。后来发现不行。因为不是找最近点。而是确定序列上对应点是不是这一轮的最近点。因为,可能有距离相等值。所以后来按照普通dij,也没超时。
还有一个更好理解的方法就是。直接dij一轮。看序列上的点是否是按照距离源点以不减的方式排列的。是的那就是true。不是就是false。
#include<bits/stdc++.h>
using namespace std;
priority_queue<int> q;
const int inf = 1<<30;
const int maxn = 1010;
int nv,ne,K;
int g[maxn][maxn];
int d[maxn];
bool vis[maxn];
vector<int> v;
bool dijkstra()
{
bool flag = true;
int cnt = 0;
fill(d,d+maxn,inf);
fill(vis,vis+maxn,false);
d[v[cnt]]=0;
for(int i=0;i<nv;i++)
{
int u = -1,minn = inf;
for(int j=1;j<=nv;j++)
{
if(vis[j]==false&&d[j]<minn)
{
u = j;
minn = d[j];
}
}
if(d[u]<d[v[cnt++]])
{
flag = false;
break;
}
if(u==-1) break;
vis[u] = true;
for(int j=1;j<=nv;j++)
{
if(vis[j]==false&&g[u][j]!=inf)
{
if(d[j]>d[u]+g[u][j])
{
d[j] = d[u]+g[u][j];
}
}
}
}
if(cnt!=nv) flag = false;
return flag;
}
int main()
{
fill(g[0],g[0]+maxn*maxn,inf);
scanf("%d%d",&nv,&ne);
v.resize(nv);
for(int i=0;i<ne;i++)
{
int u,v,dis;
scanf("%d%d%d",&u,&v,&dis);
g[u][v]=g[v][u]=dis;
}
scanf("%d",&K);
for(int i=0;i<K;i++)
{
for(int j=0;j<nv;j++)
{
scanf("%d",&v[j]);
}
if(dijkstra())
{
printf("Yes\n");
}else
{
printf("No\n");
}
}
return 0;
}
结果: