傻逼题目,毁我青春
题意:
若干人在不同地点,去zz家参加party,最先到的人可获得礼物。每个人都会走从自己家到zz家的最短路,每个人的速度都不同。同时到的话,离zz家远的获得礼物;离zz家一样远的话,序号大的人获得礼物。问最终谁获得礼物。
输入:
地址数n,人数m,边数k
k行:a, b, c 有向边的端点和权重
1个数:目标地址zz家
m个数:m个人所在地址
m个数:m个人的速度
输出:
得到礼物的人的序号
思路:最短路水题(写了三个小时WA了13发的我,看到网上都说这道题是水题,很生气,我也要说它是水题,哼)
AC代码1(SPFA+邻接矩阵):
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=305;
int g[N][N];
int p[N],v[N];
int n,m,k;
bool st[N];
int dist[N];
void spfa(int s){
memset(dist,0x3f,sizeof dist);
dist[s]=0;
queue<int>q;
q.push(s);
st[s]=true;
while(!q.empty()){
int t=q.front();
q.pop();
st[t]=false;
for(int i=1;i<=n;++i){
if(dist[i]>dist[t]+g[t][i]){
dist[i]=dist[t]+g[t][i];
if(!st[i]){
q.push(i);
st[i]=true;
}
}
}
}
}
int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){ //For each case
memset(g,0x3f,sizeof g);
memset(st,0,sizeof st);
for(int i=0;i<k;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[b][a]=min(g[b][a],c);//反向边
}
int s;
scanf("%d",&s);
for(int i=1;i<=m;++i) scanf("%d",&p[i]);
for(int i=1;i<=m;++i) scanf("%d",&v[i]);
spfa(s);
double t,mint=INF;
int ans=0;
for(int i=1;i<=m;++i){
t=1.0*dist[p[i]]/(1.0*v[i]);
if(t<mint){
mint=t;
ans=i;
}else if(t==mint){//浮点数比大小 写成<eps更严谨
if(dist[p[i]]>=dist[p[ans]]) ans=i; // >= 序号是按从小到大枚举的,距目的地距离相同的情况下,ans=i,即为了题目中的条件“序号大的人获得礼物”
}
}
if(dist[p[ans]]==INF) puts("No one");//非常严谨
else printf("%d\n",ans);
}
}
AC代码2(SPFA+邻接表)快很多,内存又少
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=305,M=5005;
int h[N],e[M],ne[M],w[M],idx;
int p[N],v[N];
int n,m,k;
bool st[N];
int dist[N];
void add(int a,int b,int c){
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void spfa(int s){
memset(dist,0x3f,sizeof dist);
dist[s]=0;
queue<int>q;
q.push(s);
st[s]=true;
while(!q.empty()){
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
}
int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
memset(h,-1,sizeof h);
memset(st,0,sizeof st);
idx=0;
for(int i=0;i<k;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(b,a,c);
}
int s;
scanf("%d",&s);
for(int i=1;i<=m;++i) scanf("%d",&p[i]);
for(int i=1;i<=m;++i) scanf("%d",&v[i]);
spfa(s);
double t,mint=INF;
int ans=0;
for(int i=1;i<=m;++i){
t=1.0*dist[p[i]]/(1.0*v[i]);
if(t<mint){
mint=t;
ans=i;
}else if(t==mint){
if(dist[p[i]]>=dist[p[ans]]) ans=i;
}
}
if(dist[p[ans]]==INF) puts("No one");
else printf("%d\n",ans);
}
}
注意:
1. 最短路部分:
求每个点到目标点的最短路——反向建图!用Dijkstra或SPFA跑从源点到其余每个点的最短路!
其他没什么好说的了,默写模板
ps: Floyd会T(不知道几组case)
2. 排序部分:
跑完最短路算法,开始“排序”。
我一开始对于No one的情况,单独写了一个循环去判断:遍历m个人的dist,如果全是INF,就输出No one,其实没必要。
然后改成了现在的写法
我原先以为这种写法不行
“因为如果某地到不了,但速度很大,t就有可能把别的能到的、但速度很小的-->因而t较大的答案更新掉
所以即便 dist[p[ans]]==INF,ans之前可能存过dist[p[ans]]<INF的合法答案,但是被冲掉了” (WA久了怀疑一切WA点)
但看下数据范围:
0<speed[i]<=100,0<n<=300, 0<m<=n, 0<k<5000,0<a,b<=n, 0<c<=100
一个能到达的地点,时间最长是5000*100/1==500000
一个到不了的地点(INF),时间最短是0x3f3f3f3f/100==1061109567/100==10611095.67,不可能更新别人
所以经过周密的数据范围的分析,这样写是可以的!
3. puts("No one\n") —— presentation error
puts相当于java里的println?自带回车啊。
4.最坑的点,题目中Input没提多组输入,但是输出部分:
Output
For each case, output the one who get the present in one line. If no one can get the present, output "No one".
有个“For each case”!太贱了。
拜拜了您!