冲刺2018模拟赛3-赛后总结
T1 冰岛
看到这道题目就想到了bfs,开始理解错了题意认为滑雪随时都可以停下于是就有了下面的代码(而且还一度把dfs和bfs记录步数的方法搞混了)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int ok=0;
int n;
int mp[2000][2000];
int x1,y1,x2,y2;
int dx[]={1,-1,0,0};
int dy[]={0,0,-1,1};
struct node{
int x,y,last_x,last_y,ans;
};
int vis[2000][2000];
inline void bfs(int x,int y){
queue<node> Q;
//ans++;
vis[x][y]=1;
Q.push({x,y,-1,-1,0});
while(!Q.empty()){
//printf("%d\n",ans);
node fr=Q.front();Q.pop();
// printf("x=%d y=%d ans=%d\n",fr.x,fr.y,ans);
for(int i=0;i<4;i++){
node l;
l.x=dx[i]+fr.x;
l.y=dy[i]+fr.y;
int sx=l.x,sy=l.y;
if(sx>n||sx<1||sy>n||sy<1||vis[sx][sy]||mp[sx][sy])continue;
vis[sx][sy]=1;
if((dx[i]!=fr.last_x||dy[i]!=fr.last_y)){
l.ans=fr.ans+1;
}
//printf("x=%d y=%d last_x=%d last_y=%d ans=%d\n",sx,sy,fr.last_x,fr.last_y,l.ans);
if(x2==sx&&y2==sy){
printf("%d",l.ans);
ok=1;
return ;
}
Q.push({sx,sy,dx[i],dy[i],l.ans});
//return ;
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>mp[i][j];
}
}
scanf("%d %d",&x1,&y1);
scanf("%d %d",&x2,&y2);
bfs(x1,y1);
if(!ok){
printf("impossible");
}
return 0;
}
幸好后面发现了是要撞墙了才能停下,于是就AC了;
总结:
1、bfs中这一步的步数因等于上一步的步数+1,所以记录步数的方法应该使用开一个数组用坐标来存当位置位于这个坐标时的步数或者像我一样用STL和struct
2、一定要认真审题!!!
3、打代码时速度不要过快,不然后面DEBUG会累死
T2保送
完全不懂,之后应了解相关概率的知识
T3A-B
将A-B=C转化为A-C=B来枚举即可
T4[遇见]
这道题目直接打了部分分,正解其实很好理解,因为隐藏的道路不要时间所以直接把隐藏的道路梭为节点即可
标程
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 300010
using namespace std;
int n,m1,m2,cnt,hl[maxn],dis[maxn],vis[maxn],cost[maxn],coin[maxn],fa[maxn];
queue<int>Q;
struct Edge{
int u,v,w,t,ne;
}e[maxn];
void add(int u,int v,int w,int t)
{
e[++cnt].u=u;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].t=t;
e[cnt].ne=hl[u];
hl[u]=cnt;
}
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void spfa(int x)
{
memset(dis,127/3,sizeof(dis));
Q.push(x);
dis[x]=0;
cost[x]=coin[x];
vis[x]=1;
while(!Q.empty()){
int u=Q.front();
Q.pop();
vis[u]=0;
for(int i=hl[u];i;i=e[i].ne){
int v=e[i].v,w=e[i].w,t=e[i].t;
if(dis[v]>dis[u]+t||(dis[v]==dis[u]+t&&cost[v]<cost[u]+w+coin[v])){
dis[v]=dis[u]+t;
cost[v]=cost[u]+w+coin[v];
if(!vis[v]){
vis[v]=1;
Q.push(v);
}
}
}
}
}
int main()
{
//freopen("meet.in","r",stdin);
//freopen("meet.out","w",stdout);
int x,y,w,t;
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m2;++i){
scanf("%d%d%d",&x,&y,&w);
int k1=find(x),k2=find(y);
if(k1!=k2){
fa[k1]=k2;
coin[k2]+=coin[k1]+w;
}
else coin[k1]+=w;
}
for(int i=1;i<=m1;++i){
scanf("%d%d%d%d",&x,&y,&w,&t);
if(find(x)!=find(y)) add(find(x),find(y),w,t);
}
spfa(find(1));
printf("%d %d\n",dis[find(n)],cost[find(n)]);
return 0;
}
解析:
tag:最短路 并查集 缩点
思路:先跑裸最短路,再考虑双向边(隐藏通道)怎么处理。既然时间花费为0,而且要最快通过,那么可以忽略两点连接的其他边,然后通道连接的两点可以缩为一点,将金币加到祖先点。这时需要灵活使用并查集,之后的每次调用都是看祖先点。