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<bits/stdc++.h>
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
2931



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



