UVA1347(Tour)(DAG上的动态规划)

UVA1347(Tour)(DAG上的动态规划)

本题的难点主要在于状态的定义,即如何去表示节点的状态。

在紫书中,作者选择了dp(i,j)来表示第一个人走到了i点,第二个人走到了j点,且编号小于max(i,j)的节点都被走过。因为dp[i][j]和dp[j][i]是重复的,因此规定i>j。这样可以保证无后效性,因为小于max(i,j)的点都被选过了,接下来就是在大于max(i,j)里面选点,所以之后的状态不会影响到前面的状态。

接下来考虑状态如何转移,由于状态的定义,下一步只能向编号大于i的节点移动,那么可以是第一个人向前走一步,或者第二个人向前走一步。从而得到状态转移方程:
dp[i][j]=min(dp[i+1][j]+dist(i,i+1),dp[i+1][i]+dist(j,i+1)),其中dist表示两点间的欧几里得距离。

递归起点是dp[2][1]。递归边界时dp[n-1][j],因为此时只剩下最后一个点没有经过,且dp[n-1][j]=dist(n-1,n)+dist(j,n)。

自然,ans=dp[2][1]+dist(1,2)。时间复杂度O为(n2)

#include
using namespace std;
const int INF=0x3f3f3f3f;
int n;
int x[1005];
int y[1005];
double dp[1005][1005];
double dist(int a,int b)
{
	return sqrt((x[b]-x[a])*(x[b]-x[a])+(y[b]-y[a])*(y[b]-y[a]));
}
double d(int i,int j)
{
	if(dp[i][j]!=0)return dp[i][j];
	dp[i][j]=min(d(i+1,i)+dist(j,i+1),d(i+1,j)+dist(i,i+1));
	return dp[i][j];
}
void solve(void)//对于dp[i][j],规定i>j 
{
	memset(dp,0,sizeof(dp));
	double di=dist(n-1,n);
	for(int i=1;i<n-1;i++)
	dp[n-1][i]=di+dist(i,n);
	double ans=d(2,1)+dist(1,2);
	printf("%.2f\n",ans);
}
int main(void)
{
//	freopen("out.txt","w",stdout);
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n;i++)
		scanf("%d%d",&x[i],&y[i]);
		solve();
	}
//	fclose(stdout);
}
//3
//1 1
//2 3
//3 1
//4
//1 1
//2 3
//3 1
//4 2

你可能感兴趣的:(ACM)