最小代价问题
题目
设有一个n×m(小于100)的方格(如图所示),在方格中去掉某些点,
方格中的数字代表距离(为小于100的数,如果为0表示去掉的点),
试找出一条从A(左上角)到B(右下角)的路径,经过的距离和为最小(此时称为最小代价),
从A出发的方向只能向右,或者向下。
Input
Output
Sample Input
4 4
4 10 7 0
3 2 2 9
0 7 0 4
11 6 12 1
Sample Output
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)
24
解题思路
这道题要用DP来做。题意是找出从左上到右下的路径,并只能往下或往右。
那么这n*m个方格中,每个方格都只可能从上面或左边来,
所以每个节点只用选择上面或左边最小的那个加起来,并记录路径就行了。
状态转移方程:f[i][j]+=min(f[i-1],f[i][j-1])+a[i][j], 2<=i<=n,2<=j<=m
路径的记录我用1来表示从上来,0表示从左来。
但是要对边界预处理,即第1行和第1列要提前处理,预处理也别忘了记录路径。
还有当前节点是0就不能走,选择的来的节点是0就不是从这个节点来的,
选择来的节点的上面和左面都是0也不是从这个节点来的。
代码
#include<iostream>
using namespace std;
long long n,m,a[101][101],b[101][101],ans[1000][2],c[101][101];
void in(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
c[i][j]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
if(i==1&&j==1)continue;
if(a[i][j]==0){//a[i]为0,跳过
a[i][j]=1000;
c[i][j]=0;
continue;
}
if(i==1)//第一行预处理
{
a[i][j]+=a[i][j-1];
}if(j==1)//第一列预处理
{
a[i][j]+=a[i-1][j];
}
}
}
}
int main(){
in();
for(int i=2;i<=n;i++){//第一行预处理的路径记录
b[i][1]=1;
}for(int j=2;j<=m;j++){//第一列预处理的路径记录
b[1][j]=0;
}
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
if(c[i-1][j]==0&&c[i][j-1]==0){//左边和上面都不能走,本节点不能走
c[i][j]=0;
}if(a[i][j]==0){//本节点为0,不能走
a[i][j]=1000;//输入的数最大为100,这样可以避免此数被选
c[i][j]=0;
}if(c[i][j]==0){
continue;
}if(i==n&&j==m){//右下终点的数是不被算的
a[i][j]=0;
}if((a[i-1][j]<=a[i][j-1])&&c[i-1][j]==1){//如果可以从上面来并上面小
a[i][j]+=a[i-1][j];
b[i][j]=1;
}else
if((a[i][j-1]<a[i-1][j])&&c[i][j-1]==1){//如果可以从左边来并左面小
a[i][j]+=a[i][j-1];
b[i][j]=0;
}
}
}
int xx,yy,x,y,l=1;
//下面是从(n,m)开始根据路径记录来往回推出具体路径
x=n;y=m;
while(x!=1||y!=1){
ans[l][0]=x;ans[l][1]=y;//ans为经过的坐标(x,y)
l++;
if(b[x][y]==1){
x--;
}else{
y--;
}
}
cout<<"(1,1)";
for(int i=l-1;i>0;i--){//倒着输出
cout<<"->"<<"("<<ans[i][0]<<","<<ans[i][1]<<")";
}cout<<endl;
cout<<a[n][m];
}