双调TSP问题总结---poj 2677和usaco5.4 canada tour

双调旅行商问题涉及从最左侧城市出发,左右遍历并返回起点,目标是优化路径。poj 2677要求最小化欧几里得距离,而usaco 5.4则需最大化访问城市数。解答中通过定义上行和下行路线的状态转移方程,将问题简化。对于poj 2677,城市可以加入上行或下行路线,选择距离最小的路径。
摘要由CSDN通过智能技术生成

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值