题意:
在$m*m$的方阵上,格子内有红、黄、无色三种颜色,由左上角走到右下角。移动时,只能走在有颜色的格子上,且跨越不同颜色的格子时花费$1$金币。可以花费$2$金币释放魔法,冷却一步,使得下一步内某无色格获得一种颜色。求走出棋盘耗费的最小金币。
程序一(15Pt):
习惯问题,我用的$n$和$m$意思和题面含义相反。
对于每个有颜色的点,讨论曼哈顿距离不超过$2$的其他有颜色的点,构建有向图(实际上每个点对会被连两次,形成了无向图),跑最短路。
然而①没有考虑终点没有颜色的情况②最短路写错了③数组开小了
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int inf=1<<30; const int N=20;////////////////////////////////////////开小了 const int M=1000; //rev to statement struct Edge{ int to,nxt,capa; }e[N*N*8*2+3]; int n,m,c[N+3][N+3],x[M+3],y[M+3]; int pnt[N+3][N+3],h[N*N+3],top; int dx1[7]={0,-1,0,0,1}; int dy1[7]={0,0,-1,1,0}; int dx2[7]={0,-2,0,0,2}; int dy2[7]={0,0,-2,2,0}; int dx3[7]={0,-1,-1,1,1}; int dy3[7]={0,-1,1,-1,1}; void add(int u,int v,int c){ top++; e[top].to=v; e[top].nxt=h[u]; e[top].capa=c; h[u]=top; } int dis[N*N+3]; int que[N*N*4+3],hd,tl; bool vis[N*N+3]; int main(){ scanf("%d%d",&n,&m); memset(c,-1,sizeof c); for(int i=1;i<=m;i++){ scanf("%d%d",&x[i],&y[i]); scanf("%d",&c[x[i]][y[i]]); } int cnt=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) pnt[i][j]=++cnt; top=-1; memset(h,-1,sizeof h); for(int p=1;p<=m;p++){ int i=x[p],j=y[p]; if(i==n&&j==n) continue; for(int k=1;k<=4;k++) if(~c[i+dx1[k]][j+dy1[k]]) add(pnt[i][j] ,pnt[i+dx1[k]][j+dy1[k]] ,c[i][j]!=c[i+dx1[k]][j+dy1[k]]); for(int k=1;k<=4;k++) if(i+dx2[k]>=0&&j+dy2[k]>=0) if(~c[i+dx2[k]][j+dy2[k]]) if(c[i+dx1[k]][j+dy1[k]]==-1) add(pnt[i][j] ,pnt[i+dx2[k]][j+dy2[k]] ,(c[i][j]!=c[i+dx2[k]][j+dy2[k]])+2); for(int k=1;k<=4;k++) if(~c[i+dx3[k]][j+dy3[k]]) if(c[i][j+dy3[k]]==-1 &&c[i+dx3[k]][j]==-1) add(pnt[i][j] ,pnt[i+dx3[k]][j+dy3[k]] ,(c[i][j]!=c[i+dx3[k]][j+dy3[k]])+2); } memset(vis,0,sizeof vis); for(int i=1;i<=cnt;i++) dis[i]=inf; dis[1]=0; hd=tl=1; que[1]=1;////////////////////起点的vis没改动 int u,v; while(hd<=tl){ u=que[hd++];////////////////////////////////////vis[u]没有改动导致错误 for(int i=h[u];~i;i=e[i].nxt){ v=e[i].to; if(dis[u]+e[i].capa<dis[v]){ dis[v]=dis[u]+e[i].capa; if(!vis[v]){ vis[v]=1; que[++tl]=v; } } } } if(dis[pnt[n][n]]==inf) printf("-1"); else printf("%d",dis[pnt[n][n]]); return 0; }
程序2(65pt):
改用DFS,①没有发现终点无色的问题②数组开小了
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int inf=1<<30; const int N=20;/////////////////////////////开小了 const int M=2000; int n,m,c[N+3][N+3]; //no color:-1 int ans[N+3][N+3]; int dx[7]={0,-1,0,0,1}; int dy[7]={0,0,-1,1,0}; bool lim(int x,int y){ return 1<=x&&x<=n &&1<=y&&y<=n; } void dfs(int x,int y,int sum,int mag){ if(x<1||x>n||y<1||y>n) return ; if(c[x][y]==-1) return ; if(sum>=ans[x][y]) return ; ans[x][y]=sum; if(x==n&&y==n) return ; int tx,ty; for(int i=1;i<=4;i++){ tx=x+dx[i]; ty=y+dy[i]; if(c[tx][ty]!=-1) dfs(tx,ty,sum+(c[x][y]!=c[tx][ty]),1); else if(mag){ c[tx][ty]=c[x][y]; dfs(tx,ty,sum+2,0); c[tx][ty]=-1; } } } int main(){ scanf("%d%d",&n,&m); memset(c,-1,sizeof c); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); c[x][y]=z; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans[i][j]=inf; dfs(1,1,0,1); if(ans[n][n]==inf) printf("-1"); else printf("%d",ans[n][n]); return 0; }
程序3(95pt):
处理终点无色问题和输出答案冲突了
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int inf=1<<30; const int N=100; const int M=2000; int n,m,c[N+3][N+3]; //no color:-1 int ans[N+3][N+3]; int dx[7]={0,-1,0,0,1}; int dy[7]={0,0,-1,1,0}; bool lim(int x,int y){ return 1<=x&&x<=n &&1<=y&&y<=n; } void dfs(int x,int y,int sum,int mag){ if(x<1||x>n||y<1||y>n) return ; if(c[x][y]==-1) return ; if(sum>=ans[x][y]) return ; ans[x][y]=sum; if(x==n&&y==n) return ; int tx,ty; for(int i=1;i<=4;i++){ tx=x+dx[i]; ty=y+dy[i]; if(c[tx][ty]!=-1) dfs(tx,ty,sum+(c[x][y]!=c[tx][ty]),1); else if(mag){ c[tx][ty]=c[x][y]; dfs(tx,ty,sum+2,0); c[tx][ty]=-1; } } } int main(){ scanf("%d%d",&n,&m); memset(c,-1,sizeof c); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); c[x][y]=z; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans[i][j]=inf; dfs(1,1,0,1); if(c[n][n]==-1) ans[n][n]=min(ans[n][n-1],ans[n-1][n])+2;////////////终点到达不了时,会出现两边都是inf,再+2 if(ans[n][n]==inf)//////////////////////////////////////inf+2代表到达不了 printf("-1"); else printf("%d",ans[n][n]); return 0; }
程序4(100pt):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int inf=1<<30; const int N=100; const int M=2000; int n,m,c[N+3][N+3]; //no color:-1 int ans[N+3][N+3]; int dx[7]={0,-1,0,0,1}; int dy[7]={0,0,-1,1,0}; bool lim(int x,int y){ return 1<=x&&x<=n &&1<=y&&y<=n; } void dfs(int x,int y,int sum,int mag){ if(x<1||x>n||y<1||y>n) return ; if(c[x][y]==-1) return ; if(sum>=ans[x][y]) return ; ans[x][y]=sum; if(x==n&&y==n) return ; int tx,ty; for(int i=1;i<=4;i++){ tx=x+dx[i]; ty=y+dy[i]; if(c[tx][ty]!=-1) dfs(tx,ty,sum+(c[x][y]!=c[tx][ty]),1); else if(mag){ c[tx][ty]=c[x][y]; dfs(tx,ty,sum+2,0); c[tx][ty]=-1; } } } int main(){ scanf("%d%d",&n,&m); memset(c,-1,sizeof c); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); c[x][y]=z; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans[i][j]=inf; dfs(1,1,0,1); if(c[n][n]==-1) ans[n][n]=min(ans[n][n-1],ans[n-1][n])+2; if(ans[n][n]==inf+2) printf("-1"); else printf("%d",ans[n][n]); return 0; }
小结:
久违的限时训练让我手忙脚乱。记得对拍!