前言
本文为CCF201903-5的思考
2019.9.3.1:00 洗完澡突然意识到自己前面都优化了行星发动机数组,后面居然没用,白白浪费了,然后改一下提交就60分了。
题目
思路
思路对于学算法的来说应该很清晰,难点是怎么写出不超时的程序得满分,如果你对思路不清晰,还请继续学习下算法。
说来,本题就是求出每个顶点到行星基地的最短路径,然后在这些最短路径里选择最短的k个累加起来输出,完成的重点在于怎么找到这些最短路径,本文最后实现的代码只拿到了30分,还是超时了,有需要的可以参考,我会继续想办法优化实现代码,会优化或者有其他思路的也欢迎留言讨论。
SPFA实现代码
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<algorithm>
using namespace std;
const int MAXV = 10010;
const int INF = 1000000000;
struct node{
int v;
int w;
node(int _v,int _w): v(_v),w(_w){};
};
int n,m,k,flag[MAXV],u,v,w,num[MAXV];
bool inq[MAXV];
vector<node> Adj[MAXV];
int d[MAXV];
bool cmp(int a,int b){
return a<b;
}
bool SPFA(int s){
memset(inq,false,sizeof(inq));
memset(num,0,sizeof(num));
fill(d,d+MAXV,INF);
queue<int> q;
q.push(s);
inq[s] = true;
num[s]++;
d[s] = 0;
while(!q.empty()){
int u = q.front();
q.pop();
inq[u] = false;
for(int j = 0;j<Adj[u].size();j++){
int v = Adj[u][j].v;
int w = Adj[u][j].w;
if(d[u]+w<d[v]){
d[v] = d[u]+w;
if(!inq[v]){
q.push(v);
inq[v] = true;
num[v]++;
if(num[v]>=n) return false;
}
}
}
}
return true;
}
int main(){
vector<int> a;
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i<=n;i++){
scanf("%d",&flag[i]);
}
for(int i = 0;i<m;i++){
scanf("%d%d%d",&u,&v,&w);
Adj[u].push_back(node(v,w));
Adj[v].push_back(node(u,w));
}
for(int i = 1;i<=n;i++){
priority_queue<int,vector<int>,greater<int> > a;
int ans = 0;
SPFA(i);
for(int j = 1;j<=n;j++){
if(flag[j] == 1&&d[j]!=INF){
a.push(d[j]);
}
}
for(int nums = 0;nums<k;nums++){
if(!a.empty()){
ans += a.top();
a.pop();
}
}
printf("%d\n",ans);
}
return 0;
}
改进
经过一点改进,只寻找行星节点的最短路径,因为从行星顶点到某个顶点的最短路径同时也是那个顶点到行星顶点的最短路径,需要用一个二维数组类似邻接矩阵来存储两顶点的距离,例如d[i][j]表示顶点i到顶点j的最短路径距离,所以可以减少一定的运行时间,最后提交代码得了35分。(能拿一点是一点)
代码实现
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<algorithm>
using namespace std;
const int MAXV = 10010;
const int INF = 1000000000;
struct node{
int v;
int w;
node(int _v,int _w): v(_v),w(_w){};
};
int n,m,k,flag[MAXV],u,v,w,num[MAXV];
bool inq[MAXV];
vector<node> Adj[MAXV];
int d[MAXV][MAXV];
bool cmp(int a,int b){
return a<b;
}
bool SPFA(int s){
memset(inq,false,sizeof(inq));
memset(num,0,sizeof(num));
queue<int> q;
q.push(s);
inq[s] = true;
num[s]++;
d[s][s] = 0;
while(!q.empty()){
int u = q.front();
q.pop();
inq[u] = false;
for(int j = 0;j<Adj[u].size();j++){
int v = Adj[u][j].v;
int w = Adj[u][j].w;
if(d[s][u]+w<d[s][v]){
d[s][v] = d[s][u]+w;
if(!inq[v]){
q.push(v);
inq[v] = true;
num[v]++;
if(num[v]>=n) return false;
}
}
}
}
return true;
}
int main(){
int start[MAXV],t=0;
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i<=n;i++){
scanf("%d",&flag[i]);
if(flag[i]==1) start[t++]=i;
}
for(int i = 0;i<m;i++){
scanf("%d%d%d",&u,&v,&w);
Adj[u].push_back(node(v,w));
Adj[v].push_back(node(u,w));
}
fill(d[0],d[0]+MAXV*MAXV,INF);
for(int i = 0;i<t;i++){
SPFA(start[i]);
}
for(int i = 1;i<=n;i++){
priority_queue<int,vector<int>,greater<int> > a;
int ans = 0;
if(flag[i] == 1){
for(int j = 1;j<=n;j++){
if(flag[j]==1&&d[i][j]!=INF){
a.push(d[i][j]);
}
}
for(int nums = 0;nums<k;nums++){
if(!a.empty()){
ans += a.top();
a.pop();
}
}
printf("%d\n",ans);
}else{
for(int j = 1;j<=n;j++){
if(flag[j] == 1&&d[j][i]!=INF){
a.push(d[j][i]);
}
}
for(int nums = 0;nums<k;nums++){
if(!a.empty()){
ans += a.top();
a.pop();
}
}
printf("%d\n",ans);
}
}
return 0;
}
持续更新优化
跟前文的区别只在于用上了优化后的行星发动机数组,在遍历输出时不用O(V^2)的时间复杂度,只需要O(VK),所以通过了60%的测试点。
此处只粘贴了跟前文区别的地方,也即是最后输出时的一个大for循环。
for(int i = 1;i<=n;i++){
priority_queue<int,vector<int>,greater<int> > a;
int ans = 0;
if(flag[i] == 1){
for(int j = 0;j<t;j++){
if(d[i][start[j]]!=INF){
a.push(d[i][start[j]]);
}
}
for(int nums = 0;nums<k;nums++){
if(!a.empty()){
ans += a.top();
a.pop();
}
}
printf("%d\n",ans);
}else{
for(int j = 0;j<t;j++){
if(d[start[j]][i]!=INF){
a.push(d[start[j]][i]);
}
}
for(int nums = 0;nums<k;nums++){
if(!a.empty()){
ans += a.top();
a.pop();
}
}
printf("%d\n",ans);
}
}
总结
光学算法只能解决问题,怎么更好地解决问题还有学会选择合适的算法和学会优化。