题目描述
有一个m×m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。
任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、 下、左、 右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1个金币。
另外, 你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。
现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?
输入输出格式
输入格式:
第一行包含两个正整数m, n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。
接下来的n行,每行三个正整数x, y,c, 分别表示坐标为(x,y)的格子有颜c。
其中c=1 代表黄色,c=0代表红色。 相邻两个数之间用一个空格隔开。 棋盘左上角的坐标为(1, 1),右下角的坐标为( m, m)
棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(1, 1) 一定是有颜色的。
输出格式:
一个整数,表示花费的金币的最小值,如果无法到达,输出−1。
输入输出样例
输入样例#1: 复制
5 7 1 1 0 1 2 0 2 2 1 3 3 1 3 4 0 4 4 1 5 5 0
输出样例#1: 复制
8
输入样例#2: 复制
5 5 1 1 0 1 2 0 2 2 1 3 3 1 5 5 0
输出样例#2: 复制
-1
思路还是比较清楚,dfs或者bfs,但是无论是哪种,必须将所有的穷举到,不能找到结果就退出!
刚开始 我的dfs +剪枝,没有通过最后5个 TLE。
我觉得主要原因是使用了visit数组,标记那些使用过的。这个比较耽误时间。最后看了别人的(见最后),恍然大悟。
#include<stdio.h>
#include<queue>
#include<vector>
#include<iostream>
#include<sstream>
#include<string.h>
#include<map>
#include<set>
#include<cmath>
#include<algorithm>
#include <climits>
using namespace std;
#define N 105
int m,n;
int c[N][N];
int xx[]= { 1,-1,0,0 };
int yy[]= { 0,0,-1,1 };
int nums[N][N];
int imin=INT_MAX;
// 坐标xy, 金币数量, 是否变色 原来颜色, 是否访问
void dfs(int x,int y,int num,int isc,int lsc,vector<bool> vist)
{
if(x==m&&y==m)
{
imin=min(imin,num);
return;
}
if(nums[x][y]<num)
return;
else
nums[x][y]=num;
//cout<<x<<" ---------- "<<y<<" ---"<<num<<endl;
for(int i=0; i<4; i++)
{
int nx=x+xx[i];
int ny=y+yy[i];
if(nx<=0||nx>m||ny<=0||ny>m)
continue;
if(vist[(nx-1)*m+ny-1])
continue;
vist[(nx-1)*m+ny-1]=true;
if(c[x][y]==c[nx][ny]&&c[x][y]>=0&&c[nx][ny]>=0)
{
dfs(nx,ny,num,0,0,vist);
} else if(c[x][y]!=c[nx][ny]&&c[x][y]>=0&&c[nx][ny]>=0)
{
dfs(nx,ny,num+1,0,0,vist);
} else if(c[x][y]<0&&c[nx][ny]>=0)
{
if(lsc==c[nx][ny])
dfs(nx,ny,num,0,0,vist);
else dfs(nx,ny,num+1,0,0,vist);
} else if(c[x][y]<0&&c[nx][ny]<0)
{
continue;
} else if(c[x][y]>=0&&c[nx][ny]<0)
{
dfs(nx,ny,num+2,1,c[x][y],vist);
}
}
};
int main()
{
cin>>m>>n;
int x,y,tc;
vector<bool> vist(m*m,0);
for(int i=1; i<=m; i++)
for(int j=1; j<=m; j++)
{
c[i][j]=-1;
nums[i][j]=INT_MAX;
}
for(int i=0; i<n; i++)
{
cin>>x>>y>>tc;
c[x][y]=tc;
}
vist[0]=true;
dfs(1,1,0,0,0,vist);
if(imin!=INT_MAX)
cout<<imin<<endl;
else
cout<<-1<<endl;
return 0;
}
最后5个超时间,我就想使用bfs,顺利通过
#include<stdio.h>
#include<queue>
#include<vector>
#include<iostream>
#include<sstream>
#include<string.h>
#include<map>
#include<set>
#include<cmath>
#include<algorithm>
#include <climits>
using namespace std;
#define N 105
int m,n;
int c[N][N];
int xx[]= {
1,-1,0,0
};
int yy[]= {
0,0,-1,1
};
typedef struct
{
int x;
int y;
int num;
int isc;
int lsc;
} Node;
Node tp;
int nums[N][N];
int imin=INT_MAX;
queue<Node> qu;
void bfs()
{
int x,y,num,isc,lsc;
if(qu.empty()) return;
tp=qu.front();
qu.pop();
x=tp.x;
y=tp.y;
isc=tp.isc;
lsc=tp.lsc;
num=tp.num;
if(x==m&&y==m)
{
imin=min(imin,num);
//return; 这里不能使用,否则找到一个结果就退出了
}
//cout<<x<<" ---------- "<<y<<" ---"<<num<<endl;
for(int i=0; i<4; i++)
{
int nx=x+xx[i];
int ny=y+yy[i];
int nlsc,nisc,nnum;
if(nx<=0||nx>m||ny<=0||ny>m)
continue;
if(c[x][y]==c[nx][ny]&&c[x][y]>=0&&c[nx][ny]>=0)
{
//dfs(nx,ny,num,0,0,vist);
nlsc=0,nisc=0,nnum=num;
} else if(c[x][y]!=c[nx][ny]&&c[x][y]>=0&&c[nx][ny]>=0)
{
//dfs(nx,ny,num+1,0,0,vist);
nlsc=0,nisc=0,nnum=num+1;
} else if(c[x][y]<0&&c[nx][ny]>=0)
{
if(lsc==c[nx][ny])
{ //dfs(nx,ny,num,0,0,vist);
nlsc=0,nisc=0,nnum=num;
}
else
{
//dfs(nx,ny,num+1,0,0,vist);
nlsc=0,nisc=0,nnum=num+1;
}
} else if(c[x][y]<0&&c[nx][ny]<0)
{
continue;
} else if(c[x][y]>=0&&c[nx][ny]<0)
{
//dfs(nx,ny,num+2,1,c[x][y],vist);
nlsc=c[x][y],nisc=1,nnum=num+2;
}
if(nums[nx][ny]<=nnum)
continue;
else
{
nums[nx][ny]=nnum;
tp.x=nx;
tp.y=ny;
tp.isc=nisc;
tp.lsc=nlsc;
tp.num=nnum;
qu.push(tp);
}
}
bfs();
};
int main()
{
cin>>m>>n;
int x,y,tc;
vector<bool> vist(m*m,0);
for(int i=1; i<=m; i++)
for(int j=1; j<=m; j++)
{
c[i][j]=-1;
nums[i][j]=INT_MAX;
}
for(int i=0; i<n; i++)
{
cin>>x>>y>>tc;
c[x][y]=tc;
}
vist[0]=true;
tp.x=1;
tp.y=1;
tp.lsc=0;
tp.isc=0;
tp.num=0;
qu.push(tp);
bfs();//
if(imin!=INT_MAX)
cout<<imin<<endl;
else
cout<<-1<<endl;
return 0;
}
另外,网上别的dfs代码,很明朗,链接为https://www.luogu.org/problemnew/solution/P3956
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define inf 0x7fffffff
int fx[4] = {-1, 0, 1, 0}; // x偏移量
int fy[4] = {0, -1, 0, 1}; // y偏移量
int f[110][110]; // 存储每个点的最优解
int mp[110][110]; // 存储初始棋盘
int m, n, ans = inf;
void dfs(int x, int y, int sum, bool frog)
{
if(x < 1 || y < 1 || x > m || y > m) return; // 超出边界
if(mp[x][y] == 0) return; // 无色区域
if(sum >= f[x][y]) return; // 不符合最优解
// 以上三种情况剪枝
f[x][y] = sum;
if(x==m && y==m) // 终点,坐标为(m, m)
{
if(sum < ans) ans = sum; // 更新最优解
return;
}
for(int i = 0; i < 4; ++i)
{
int xx = x + fx[i];
int yy = y + fy[i];
if(mp[xx][yy]) // 若下一格有色
{
if(mp[xx][yy] == mp[x][y]) // 若颜色相同
dfs(xx, yy, sum, false); // 则不花钱
else dfs(xx, yy, sum+1, false); // 否则花费+1
} else // 否则(若下一格无色)
if(!frog) // (且)没有用过魔法
{
mp[xx][yy] = mp[x][y];
dfs(xx, yy, sum+2, true); // 使用魔法
mp[xx][yy] = 0;
}
}
}
int main()
{
memset(f, 0x7f, sizeof(f));
scanf("%d %d", &m, &n);
for(int i = 1; i <= n; ++i)
{
int x, y, c;
scanf("%d %d %d", &x, &y, &c);
mp[x][y] = c + 1;
// 1红色 2黄色 0无色(未赋值,其初值为0)
}
dfs(1, 1, 0, false);
printf("%d", ans==inf ? -1 : ans);
return 0;