给出一个m行n列(m≤10,n≤100)的整数矩阵,从第一列任意一个位置出发,每次可以往右、右上或右下走一格,最终到达最后一列。
要求经过整数之和最小。矩阵是环形的,即最后一行向下是第一行。输出路径上每列的行号。多解时,输出字典序最小的。
下图是两个矩阵的最优路线
Sample Input
5 6
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6
3 7 2 8 6 4
5 6
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6
3 7 2 1 2 3
2 2
9 10 9 10
Sample Output
1 2 3 4 4 5
16
1 2 1 5 4 5
11
1 1
19
转移方程很简单,设d(i,j)为从格子(i,j)出发到最后一列的最小开销。则:
d(i,j)=d(i,j)+min{d(i-1,j-1),d(i,j-1),d(i+1,j-1)}
主要问题在于输出路径。这就需要在计算d(i,j)时记录 " 下一列的行号 " 的最小值。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxx=10000; int G[maxx][maxx]; int dp[maxx][maxx]; int n,m,ar[3]; int compare_min(int a,int b,int c){ ar[0]=a,ar[1]=b,ar[2]=c; sort(ar,ar+3); return ar[0]; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>G[i][j]; for(int j=0,i=1;i<=n;i++) dp[i][j]=0; for(int j=1;j<=m;j++) for(int i=1; i<=n; i++) { if(i==1) dp[i][j]=G[i][j]+compare_min(dp[n][j-1],dp[i][j-1],dp[i+1][j-1]); else if(i==n) dp[i][j]=G[i][j]+compare_min(dp[i-1][j-1],dp[i][j-1],dp[1][j-1]); else dp[i][j]=G[i][j]+compare_min(dp[i-1][j-1],dp[i][j-1],dp[i+1][j-1]); } int ans=(1<<30); for(int j=m,i=1;i<=n;i++) if(ans>dp[i][j]) ans=dp[i][j]; cout<<ans<<endl; int road[m],cns=0; for(int j=m;j>=1;j--) { for(int i=1;i<=n;i++) { if(dp[i][j]==ans) { road[cns]=i; break; } } ans=ans-G[road[cns]][j]; cns++; } for(int i=m-1;i>=0;i--) cout<<road[i]<<" ";; cout<<endl; return 0; }