D. Explorer Space (CF#718 (Div. 1 + Div. 2))
题意
给你一个 n × m n \times m n×m 的矩阵与数字 k k k,其中每个点都和它相邻的四个点有相连的边,给定所有边的权值。要求你给出一个矩阵,其中第 i i i 列第 j j j 行表示从原矩阵第 i i i 列第 j j j 行出发,走 k k k 步并回到这个点,经过的路的最小边权和是多少。
思路
参考:Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2) - ccsu_madoka
首先肯定 k % 2 = 0 k \% 2 = 0 k%2=0 ,所以奇数情况直接输出全-1。那么问题就变成了走出去 k / 2 k / 2 k/2 步的最小边权和。
开始想用 d i j s t r a dijstra dijstra ,觉得复杂度不太行放弃(而且这个是 k k k步而不是最短路其实)
后面想到深搜,于是用写了一半的最短路改了一个深搜,并且为了这么写把 i , j i, j i,j 改成了一维 i + j ∗ M i + j * M i+j∗M。最后发现这样的方法其实还是需要不停的重新初始化,因为每次源点都会变动,遂作罢。
最后参考了上面链接里的记忆化搜索,还是把深搜改成了三维数组的深搜,并用 d i s i , j , k dis_{i, j, k} disi,j,k 来表示从 i i i 出发,到 j j j ,走 k k k 步的最小边权和。
很久没有写这样的深搜了,很爽,这题很值得反复看看(嗯
代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
typedef pair<int, int> P;
const int N = 1e6 + 10;
const int M = 550;
typedef long long ll;
int n, m;
int dis[M][M][20];//dp
int co[M][M][4];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
inline bool ok(int x, int num)
{
return x <= num && x >= 1;
}
int dfs(int x, int y, int k)
{
if(k <= 0)
return 0;
if(dis[x][y][k] != -1)
return dis[x][y][k];
int minn = INF;
for(int i = 0; i < 4; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
if(ok(nx, n) && ok(ny, m))
{
minn = min(minn, dfs(nx, ny, k - 1) + co[x][y][i]);
}
}
return dis[x][y][k] = minn;
}
int main()
{
memset(dis, -1, sizeof(dis));
int k;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++)
{
for(int j = 1, x; j <= m - 1; j++)
{
cin >> x;
co[i][j + 1][1] = x;
co[i][j][3] = x;
}
}
for(int i = 1; i <= n - 1; i++)
{
for(int j = 1, x; j <= m; j++)
{
cin >> x;
co[i + 1][j][0] = x;
co[i][j][2] = x;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(k % 2)
{
cout << "-1 ";
continue;
}
cout << dfs(i, j, k / 2) * 2 << ' ';
}
cout << endl;
}
return 0;
}