116 - Unidirectional TSP

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

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 ;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值