双调TSP指的就是从最左边的城市出发,从左往右遍历一些城市,到达最右端,再从最右端从右往左返回出发城市,然后最优化某些东西。
poj 2677 就是要遍历所有的城市,最小化行走的距离(欧几里德距离)
usaco canada tour 就是每个城市最多遍历一次(出发点两次),最大化遍历的城市数量。
【解答】
显然从左往右,从右往左都是一样的,都可以变成从左往右。我们称前者为上行路线,后者为下行路线。
状态f[i][j]表示从出发点出发,上行路线最右端为i,下行路线最右端为j,同时我们规定,i在j的右边。
这样定义状态后,问题便迎刃而解。
例如poj 2677:
如果第i个城市归入上行路线,则 f[i][j]=min(f[i][j],f[i-1][j]+dis(a[i-1],a[i]));
否则归入下行路线:f[i][i-1]=min(f[i][i-1],f[i-1][j]+dis(a[j],a[i]));
这样,我们就把这个问题解决了。
【poj 2677 代码】
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=500;
const double INF=1e10;
struct point
{
int x,y;
}a[N];
double f[N][N];
double dis(point a,point b)
{
return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
int main()
{
int n,i,j;
double ans;
freopen("in","r",stdin);
while (scanf("%d",&n)!=EOF)
{
for (i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
f[i][j]=INF;
f[1][1]=0;
for (i=2;i<=n;i++)
for (j=1;j<i;j++)
{
f[i][j]=min(f[i][j],f[i-1][j]+dis(a[i-1],a[i]));
f[i][i-1]=min(f[i][i-1],f[i-1][j]+dis(a[j],a[i]));
}
ans=INF;
for (i=1;i<=n;i++)
ans=min(ans,f[n][i]+dis(a[n],a[i]));
printf("%.2f\n",ans);
}
}
【canada tour 代码】
/*
ID: ascii991
PROG: tour
LANG: C++
*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int N=103;
map<string,int> Map;
int f[N][N];
bool a[N][N];
int main()
{
int i,j,k,m,n,ans=1;
string x,y;
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
cin >> n >> m;
for (i=1;i<=n;i++)
{
cin >> x;
Map[x]=i;
}
for (i=1;i<=m;i++)
{
cin >> x >> y;
a[Map[x]][Map[y]]=a[Map[y]][Map[x]]=true;
}
f[1][1]=1;
for (k=2;k<=n;k++)
{
for (i=1;i<k;i++)
for (j=1;j<=i;j++)
if (f[i][j]>0)
{
if (a[k][i]) f[k][j]=max(f[k][j],f[i][j]+1);
if (a[k][j]) f[k][i]=max(f[k][i],f[i][j]+1);
}
}
for (i=1;i<=n;i++)
if (a[n][i])
ans=max(ans,f[n][i]);
cout << ans << endl;
}