TSP问题 MTSP 双调TSP 商旅问题 中国邮差问题 担货郎问题

 

1.1 TSP商旅问题 coj1133(TSP是NP难问题一般只会到16)

题意:某乡有n个村庄(16>n>1),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(1000>s>0)是已知的,且A村到B村与B村到A村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

代码:

#include <stdio.h>

#include <string.h>

#include<iostream>

using namespace std;

const int SIZE= 16;

const int MAX= 1000*SIZE;

int dp[SIZE+2][(1<<SIZE)+10], w[SIZE+3][SIZE+3];

int run(int n, int s)

{

    int sta= 1<<(n+1);

    for(int i= 0; i<= n; i++)

        for(int j= 0; j< sta; j++)    dp[i][j]= MAX;

    dp[s][1<<s]= 0;

    //对于n个点会有2^(n+1)种状态

    //代表的状态

    for(int i= 0; i< sta; ++i)

        for(int j= 0; j<= n; ++j)

        {

                     //1<<j==2^j

                     //右移位j个单位判断当前状态i城市是否在考虑范围中

            if(((1<<j)&i)== 0)    continue;

            for(int k= 0; k<= n; ++k)

            {

                //如果点没有访问过且可以更新

                if(((1<<k)&i)== 0 )   

                            dp[k][(1<<k)|i]= min(dp[k][(1<<k)|i],dp[j][i]+ w[j][k]);

            }

        }

    return dp[n][sta-1];

}

int main()

{

       int n;

    while(~scanf("%d", &n))

    {

        for(int i= 0; i< n; i++)

        {

            for(int j= 0; j< n; j++)   

                     scanf("%d", &w[i][j]);

          //拆点,因为最后形成的是环,所以将第一个点加到后面n

          //如果跑了一遍最短路就可以不用拆点

                  w[i][n]= w[i][0];

        }

        int ans= run(n, 0);

        printf("%d\n", ans);

    }

    return 0;

}

 

1.2 双调TSPpoj2677

题意:双调TSP指的就是从最左边的城市出发,从左往右遍历一些城市,到达最右端,再从最右端从右往左返回出发城市,然后最优化某些东西。

#include<iostream>

#include<cmath>

#include<cstdio>

#include<cstring>

#define MAX 0x7fffff

using namespace std;

const int maxn=211;

struct

{

    int x,y;

}a[maxn];

int n;

double d[maxn][maxn],p[maxn][maxn];

void qsort(int l,int r)

{

    int i=l,j=r,mid=a[(i+j)>>1].x;

    while (i<j)

    {

        while (a[i].x<mid) i++;

        while (mid<a[j].x) j--;

        if (i<=j)

        {

            swap(a[i].x,a[j].x);

            swap(a[i].y,a[j].y);

            i++; j--;

        }

    }

    if (l<j) qsort(l,j);

    if (i<r) qsort(i,r);

}

 

double cnt(int x1,int y1,int x2,int y2)

{

    int x=(x1-x2)*(x1-x2);

    int y=(y1-y2)*(y1-y2);

    return sqrt(x+y);

}

 

void work_p()

{

    for (int i=1;i<n;i++)

        for (int j=i+1;j<=n;j++)

            p[i][j]=p[j][i]=cnt(a[i].x,a[i].y,a[j].x,a[j].y);

}

 

void DP()

{

    d[1][1]=0;

    for (int i=2;i<=n;i++)

        d[i][1]=p[i][1];

    for (int i=2;i<n;i++)

    {

        d[i+1][i]=MAX;

        for (int j=1;j<=i-1;j++)

        {

            d[i+1][j]=d[i][j]+p[i][i+1];

            d[i+1][i]=min(d[i+1][i],d[i][j]+p[j][i+1]);

        }

    }

}

int main()

{

      

    //freopen("data.in","r",stdin);

    //freopen("data.out","w",stdout);

    while(scanf("%d",&n)!=EOF)

    {

    for (int i=1;i<=n;i++)

        scanf("%d%d",&a[i].x,&a[i].y);

    qsort(1,n);

    work_p();

    DP();

    printf("%.2lf\n",d[n][n-1]+p[n][n-1]);

       }

    return 0;

}

 

 

1.3 Mtsp问题 hdu4281

摘自:http://blog.csdn.net/woshi250hua/article/details/7961869

题目大意:给定n个地点的坐标和每个地点的权值,即一张图n个点,点有点权边有边权。现在裁判在点1,需要分配这些裁判到这些点去,已知每个裁判能够到点权之和不大于m,而且一个点不能由两个裁判访问。现在给出两个问题,1、最少几个裁判可以覆盖所有点 2、给定无数个裁判,怎么样访问这些点使得总边权和最小,裁判访问完必须回到1点,而且一个裁判访问的点权之和不能超过m

解题思路: 昨天天津赛区的1004题,比赛的时候都想到了算法,就是不敢去敲,一直在想稳妥的算法,最终没有ac

    晚一点和其他学校的acmer交流聊到这题,就想着要不要去试下,如果不行的话明天去搜论文,因为这是很经典的mTSP问题。但是没想到竟然ac了,神奇得ac了,坑爹地ac了,复杂度O(2^(2*n)+(2^n*n^2)),排名还很靠前。ac完想到的第一句话不是终于ac而是:尼玛,中山大学就喜欢这么暴力么?..

    有可能不是正解,但还是写下思路,感觉两个问题都很经典。 

    第一问:求最少的裁判覆盖这些点,思路是先将2^n种地点的选择集合压缩成2^n个物品,物品的权值为集合内的点权之和,如果总和<=m,那么他是一种合法的组合,存起来。这样就得到tot种合法组合,对这tot种组合进行01背包,dp[i]表示容量为i时的最小费用,和常规的背包不同,但本质是一样的。状态转移方程:dp[i] = min(dp[i],dp[j]+1) (ji的子集,i = j | state[k]并且jstate[k]没有交集,state[k]表示第k个合法物品)

    PS:后来发现这一问其实用贪心就可以解决,每次都选最大的,直到本次不能选为止,看能选几次.

    第二问:多旅行商问题即mTsp,感觉挺经典的,思路是将mtsp转化成普通的tsp,然后再将各个tsp合并成答案。先要O(2^n*n^2)的预处理得到np[i]表示一个裁判走的集合为i的所有地点又回到最初的点的最少权值和,然后np[i] = min(np[i],np[k|(1<<0)]+np[(i-k)|(1<<0)])(i必须包含0节点,因为子集可能不含0节点,所以要和1<<0或起来,这样才是将两个裁判所走的边权和合并)

#include <stdio.h>

#include <string.h>

#include <math.h>

#include <algorithm>

using namespace std;

#define MIN (1<<17)

#define MAX 110000

#define INF (1<<29)

#define min(a,b) ((a)<(b)?(a):(b))

int tot, ans1, ans2, n, m; //总合法物品数,第一、第二问答案

int x[20], y[20], val[20]; //左边和点权

int dp[MAX], state[MIN]; //第一问用到

int map[20][20], isok[MIN]; //边权、合法物品集合

int cost[17][MIN], np[MIN]; //第二问用到

void Initial()

{

       int i, j, k;

    tot = 0;

    memset(map, 0, sizeof (map));

    for (i = 0; i < (1 << n); ++i)

        dp[i] = np[i] = INF;

    for (i = 0; i <= n; ++i)

        for (j = 0; j < (1 << n); ++j)

            cost[i][j] = INF;

    cost[0][1] = 0;

}

int cmp1(int a, int b)

{

    return a > b;

}

int Solve_Tanxin()

{

    int i, j, k, mmin = INF;

    int tp[20], vis[20];

    for (i = 0; i < n; ++i)

        vis[i] = 0, tp[i] = val[i];

    sort(tp, tp + n, cmp1);

    for (i = 1; i <= n; ++i)

       {

        int rest = m;

        for (j = 0; j < n; ++j)

            if (!vis[j] && tp[j] <= rest)

                rest -= tp[j], vis[j] = 1;        ;

        for (j = 0; j < n && vis[j] == 1; ++j);

        if (j == n) return i;

    }

    return INF;

}

int Solve_First()

{

    int i, j, k, mmin = INF;

    dp[0] = 0;

    for (i = 0; i < tot; ++i)

        for (j = (1 << n) - 1; j >= 0; --j)

              {

 

            if (dp[j] == INF) continue;

            int st = j + state[i];

            if (st != (j | state[i])) continue;

            dp[st] = min(dp[st], dp[j] + 1);

        }

    return dp[(1 << n) - 1];

}

void GetDist()

{

    for (int i = 0; i < n; ++i)

        for (int j = i + 1; j < n; ++j)

              {

            double xx = x[i] - x[j];

            double yy = y[i] - y[j];

            xx *= xx, yy *= yy;

            map[i][j] = map[j][i] = ceil(sqrt(xx + yy));

        }

}

int ok(int x)

{

    int sum = 0, i;

    for (i = 0; i < n; ++i)

        if (x & (1 << i)) sum += val[i];

    return sum <= m;

}

int TSP_Second()

{

    int i, j, k;

    GetDist();

    for (i = 1; i < (1 << n); ++i)

       if (isok[i])

       {

        for (j = 0; j < n; ++j)

              if (i & (1 << j))

              {

                     np[i] = min(np[i], cost[j][i] + map[j][0]);

            for (k = 0; k < n; ++k)

                     if ((i & (1 << k)) == 0)

            cost[k][i | (1 << k)] = min(cost[k][i | (1 << k)], cost[j][i] + map[j][k]);

        }

    }

       /*要保证第一个点是老师的点。因为可能在tsp的时候从任意点出发的

       但是题目要求第一个点出发,所以如果求的时候不是第一个点那么就

       加上到第一个点的距离,在判断tsp */

    for (i = 1; i < (1 << n); ++i)

        if (i & 1) for (j = (i - 1) & i; j; j = (j - 1) & i)

                np[i] = min(np[i], np[j | 1] + np[(i - j) | 1]);

    return np[(1 << n) - 1];

}

int main()

{

    int i, j, k;

    while (scanf("%d%d", &n, &m) != EOF)

       {

        Initial();

        for (i = 0; i < n; ++i)

            scanf("%d%d", &x[i], &y[i]);

        for (i = 0; i < n; ++i)

            scanf("%d", &val[i]);

        for (i = 1; i < (1 << n); ++i)

              {

            isok[i] = ok(i);

            if (isok[i]) state[tot++] = i;

        }

        ans1 = Solve_Tanxin();

        //ans1 = Solve_First();

        if (ans1 == INF)

            ans1 = ans2 = -1;

        else ans2 = TSP_Second();

        printf("%d %d\n", ans1, ans2);

    }

}

你可能感兴趣的:(算法,优化,struct,REST,ini)