题目链接: https://cn.vjudge.net/contest/306123#problem/B
题目解析:
(1)给一个m行n列(m≤10,n≤100)的整数矩阵,从第一列任何一个位置出发每次往右、右
上或右下走一格,最终到达最后一列。要求经过的整数之和最小。整个矩阵是环形的,即第
一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解时输
出字典序最小的
(2)一个点可以推出三个状态是关键,同时还需要记录路径
1.如果是最后一列,每一点的状态直接等于它的权值大小
if(j==m-1)//最后一列``
dp[i][j]=st[i][j];
2.从正数第二列到倒数第二列,点扩张的时候要注意第一行和最后一行,比找到三个中的最小值
int row[3]= {i,i-1,i+1}; //三个方向
if(i==0) //第一行
row[1]=n-1;
if(i==n-1) //最后一行
row[2]=0;
sort(row,row+3);
dp[i][j]=inf;
for(int k=0; k<3; k++)
{
int v=st[i][j]+dp[row[k]][j+1];
if(dp[i][j]>v)
{
dp[i][j]=v;
next[i][j]=row[k];//从倒数第一列存到正数第二列,正数第一列没存
}
3.对第一列的判断
if(j==0&&ans>dp[i][j])
{
ans=dp[i][j];
first=i; //第一列最小权值的横坐标-1
}
完整代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
int n, m;
int a[30][300], dp[30][300]; //a为地图
int nex[30][300];
void TSP(){
int ans = inf, first = 0;
for (int j = n - 1; j >= 0; j--) //从最后一列到第一列
{
for (int i = 0; i < m; i++){
if (j == n - 1) //最后一列
dp[i][j] = a[i][j];
else{
int row[3] = { i,i - 1,i + 1 }; //一个点可以从上一列的三个点扩展来 ,row[3]就是就是要输出的行值
if (i == 0) //对环形的处理
row[1] = m - 1;
if (i == m - 1)
row[2] = 0;
sort(row, row + 3);//先排序可以确保优先选择字典序小的行
dp[i][j]=inf;
for (int k = 0; k<3; k++)//这里求三种决策的最优解
{
int v=dp[row[k]][j+1] + a[i][j];
if (dp[i][j]>v){
dp[i][j] = v;
nex[i][j] = row[k];
}
}
}
//printf("dp[i][j]的值为---->%d,坐标为(%d,%d)\n",dp[i][j],i,j);
if(j==0&&ans>dp[i][j])//这里求从第一列的哪一行出发的最优解
{
ans = dp[i][j];
first = i;
}
}
}
printf("%d", first + 1);
int i = nex[first][0];
for (int j = 1; j < n; j++)//正序输出的就是路径
{
printf(" %d", i + 1);
i = nex[i][j];
}
printf("\n%d\n", ans);
}
int main(){
while (scanf("%d%d", &m, &n) != EOF)//注意行列输入对应的m和n
{
for (int i = 0; i < m; i++) //输入地图
for (int j = 0; j < n; j++)
scanf("%d", &a[i][j]);
TSP();
}
return 0;
}