http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=114&page=show_problem&problem=52
题目意思很明确,并且这一题的递归方程很好写,只不过需要注意的是对于字典序的理解。在更新路径的时候要不断的比较字典序,即要从头到尾的比较而不是仅仅比较前驱。
代码如下:
/*
ID: csuchenan
PROG: uva 116 - Unidirectional TSP
LANG: C++
algorithm: dynamic program
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stack>
using namespace std ;
int map[15][105] ; //15行105列,存储矩阵
int dpm[105][15] ; //105行15列,存储对应的最小值
int seq[105][15] ; //105行15列,存储序列
bool judge(int x , int y , int len) ; //判断字典序大小
int row ;
int col ;
int main()
{
//freopen("116.in" , "r" , stdin) ;
//freopen("116.out" , "r" , stdout) ;
while(cin>>row>>col)
{
int i ;
int j ;
memset(map , 0 , sizeof(map)) ;
memset(dpm , 0 , sizeof(dpm)) ;
memset(seq , 0 , sizeof(seq)) ;
for(i = 0 ; i < row ; i ++)
for(j = 0 ; j < col ; j ++)
cin>>map[i][j] ;
for(i = 0 ; i < row ; i ++)
dpm[0][i] = map[i][0] ;
int k ;
//主要的dp过程,尤其注意对字典序的判断
for(i = 1 ; i < col ; i ++)
{
for(j = 0 ; j < row ; j ++)
{
k = (row + j - 1) % row ;
dpm[i][j] = dpm[i-1][k] + map[j][i] ;
seq[i][j] = k ;
if(dpm[i][j] > dpm[i-1][j] + map[j][i])
{
dpm[i][j] = dpm[i-1][j] + map[j][i] ;
seq[i][j] = j ;
}
else if(dpm[i][j] == dpm[i-1][j] + map[j][i] && judge(seq[i][j] , j , i - 1))
seq[i][j] = j ;
k = (j + 1)%row ;
if(dpm[i][j] > dpm[i-1][k] + map[j][i])
{
dpm[i][j] = dpm[i-1][k] + map[j][i] ;
seq[i][j] = k ;
}
else if(dpm[i][j] == dpm[i-1][k] + map[j][i] && judge(seq[i][j] , k , i - 1))
seq[i][j] = k ;
}
}
int nmin ;
nmin = 0 ;
for(i = 1 ; i < row ; i ++)
{
if(dpm[col-1][i] < dpm[col-1][nmin])
nmin = i ;
else if(dpm[col-1][i] == dpm[col-1][nmin] && judge(nmin , i , col - 1))
nmin = i ;
}
stack<int> a ;
i = nmin ;
j = col - 1 ;
while(j > 0)
{
i = seq[j][i] ;
a.push(i) ;
j -- ;
}
while(!a.empty())
{
cout<< a.top() + 1 <<" " ;
a.pop() ;
}
cout<<nmin + 1<<endl ;
cout<<dpm[col-1][nmin]<<endl ;
}
return 0 ;
}
//判断字典序
bool judge(int x , int y , int len)
{
int i ;
int j ;
stack<int> a ;
stack<int> b ;//用栈缓存线路数据
a.push(x) ;
b.push(y) ;
while(len > 0)
{
x = seq[len][x] ;
y = seq[len][y] ;
a.push(x) ;
b.push(y) ;
len -- ;
}
while(!a.empty())
{
if(a.top() > b.top())
return true ;
else if(a.top() < b.top())
return false ;
a.pop() ;
b.pop() ;
}
return false ;
}
这个题目当然还可以逆序动规,这样动规的时候可以很容易保证字典序,而不用编写专门的函数来验证字典序问题。代码如下:
/*
ID: csuchenan
prog: uva 116 - Unidirectional TSP
LANG: C++
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std ;
const int maxn = 105 ;
int map[15][maxn] ;
int dpm[15][maxn] ;
int seq[15][maxn] ;
int row ;
int col ;
int main()
{
// freopen("116.in" , "r" , stdin) ;
// freopen("116.out" , "w" , stdout) ;
while(cin>>row>>col)
{
int i ;
int j ;
int k ;
memset(map , 0 , sizeof(map)) ;
memset(dpm , 0 , sizeof(dpm)) ;
memset(seq , 0 , sizeof(seq)) ;
for(i = 0 ; i < row ; i ++)
for(j = 0 ; j < col ; j ++)
cin>>map[i][j] ;
for(j = 0 ; j < row ; j ++)
dpm[j][col-1] = map[j][col-1] ;
for(i = col - 2 ; i >= 0 ; i --)
{
for(j = 0 ; j <= row ; j ++)
{
//ÓÒÉÏ
k = (j - 1 + row) % row ;
dpm[j][i] = dpm[k][i+1] + map[j][i] ;
seq[j][i] = k ;
//ÓÒ²à
if(dpm[j][i] > dpm[j][i+1] + map[j][i] )
{
dpm[j][i] = dpm[j][i+1] + map[j][i] ;
seq[j][i] = j ;
}
else if(dpm[j][i] == dpm[j][i+1] + map[j][i] && seq[j][i] > j)
{
seq[j][i] = j ;
}
//ÓÒϲà
k = (j + 1) % row ;
if(dpm[j][i] > dpm[k][i+1] + map[j][i] )
{
dpm[j][i] = dpm[k][i+1] + map[j][i] ;
seq[j][i] = k ;
}
else if(dpm[j][i] == dpm[k][i+1] + map[j][i] && seq[j][i] > k)
{
seq[j][i] = k ;
}
}
}
int nmin ;
nmin = 0 ;
for(i = 1 ; i < row ; i ++)
{
if(dpm[i][0] < dpm[nmin][0])
nmin = i ;
else if(dpm[i][0] == dpm[nmin][0] && nmin > i)
nmin = i ;
}
j = 0 ;
i = nmin ;
while(j < col - 1)
{
cout<<i + 1<<" " ;
i = seq[i][j] ;
j ++ ;
}
cout<<i + 1 <<endl ;
cout<<dpm[nmin][0]<<endl ;
}
return 0 ;
}

本文深入探讨了UVA在线评测平台上的116号问题,即无向点到点最短路径问题。文章详细解释了动态规划算法的实现过程,特别是如何通过递归方程解决字典序问题,以及在路径更新过程中确保字典序正确性的关键步骤。此外,还提供了两种不同的动规解决方案,一种是传统的正向动规,另一种是逆序动规,后者在保证字典序方面更为直观。
1113

被折叠的 条评论
为什么被折叠?



