读书人

ACM 题目求最优解 小弟我TLE了啊

发布时间: 2012-02-26 20:19:45 作者: rapoo

ACM 题目求最优解 我TLE了求助啊.
描述:
在二维坐标上给你M个点(M是偶数)的坐标,坐标都是整数,你可以任意联接其中两点(不管中间有没有障碍),这两点就消失了(和游戏里的一样).但消去两点的路径和两个点的位置有关,也就是说路径的长度等于两点X轴与Y轴差的绝对值之和.比如一个点坐标为(10,10),另外一个点坐标为(2,3),那么消去这两个点的路径长度为8+7=15.问消去所有点的路径长度之和最小值是多少?

第一行输一个正整数N,下面有N种连连看的地图

每种地图的第一行输入一个正整数M
(M是偶数,并且2=< M <=20),代表地图上有M个点.
下面有M行,每一行都有两个整数,代表这个点的X轴坐标与Y轴坐标.
(坐标的绝对值不会大于十万)

输出共N行,每行都是消去所有点的路径长度之和的最小值.

Sample Input
3

2
10 10
2 3

2
0 1
0 2

4
0 2
0 3
0 5
0 6

Sample Output
15
1
2

-------------------------
我的方法 :

C/C++ code
 #include <stdio.h> struct Point  {      int x, y;  }PointList[21]; int g_nSatusList[1024][1024]; int g_nPointList[21][21];  int g_nFlagList[21] = {0};  int n = 0;  int valuex = 1023, valuey = 1023;int max1 = 0, max2 = 0; int abs(int argA, int argB)  {      return argA > argB ? argA - argB : argB - argA;  }int DeepInto(int Limit)  {      int i = 0 ,j = 0;      if (Limit == 2)      {          int x = -1, y = -1;          for (i = 0; i < n; i++)          {              if (g_nFlagList[i] == 0)              {                  if (x == -1)                     x = i;                 else                     y = i;             }           }          return g_nPointList[x][y] == 0 ? g_nPointList[y][x] : g_nPointList[x][y];      }    int minitemp = 200000000;    int tpj = -1;    for (i = 0; i < n; i++)      {          if (g_nFlagList[i] == 0)          {              if (tpj == -1)                tpj = i; //记录开始点            g_nFlagList[i] = 1;                          if (i < 10)                 valuex = (valuex ^ (1 << i));             else                 valuey = (valuey ^ (1 << (i - 10)));             int j = 0;            for (j = tpj; j < n; j++)              {                  if (g_nFlagList[j] == 0 && minitemp > g_nPointList[i][j])                  {                      g_nFlagList[j] = 1;                                           if (j < 10)                         valuex = (valuex ^ (1 << j));                     else                         valuey = (valuey ^ (1 << (j - 10)));                     int temp;                     if (g_nSatusList[valuex][valuey] != -1)                     {                         temp = g_nSatusList[valuex][valuey];                     }                     else                     {                        temp = DeepInto(Limit - 2);                     }                    int ijLength = g_nPointList[i][j] == 0 ? g_nPointList[j][i] : g_nPointList[i][j];                    if (minitemp > ijLength + temp)                      {                          minitemp = ijLength + temp;                      }                     if (g_nSatusList[valuex][valuey] == -1 ||                         g_nSatusList[valuex][valuey] > minitemp)                         g_nSatusList[valuex][valuey] = minitemp;                     g_nFlagList[j] = 0;                      if (j < 10)                         valuex = (valuex ^ (1 << j));                     else                         valuey = (valuey ^ (1 << (j - 10)));                 }              }              g_nFlagList[i] = 0;              if (i < 10)                 valuex = (valuex ^ (1 << i));             else                 valuey = (valuey ^ (1 << (i - 10)));         }      }      return minitemp;  }  void initailArray(int MX, int MY) {     int i = 0, j = 0;     for (;i < MX; i++)         for(j = 0; j < MY; j++)             g_nSatusList[i][j] = -1; } int main ()  {       int CaseN = 0;      while (1 == scanf("%d", &CaseN))      {          while (CaseN--)          {              scanf("%d", &n);              int i = n;            max1 = 0, max2 = 0;            while (i--)              {                  scanf("%d %d", &PointList[i].x, &PointList[i].y);                if (i < 10)                    max1 = max1 | (1 << i);                else                    max2 = max2 | (1 << (i - 10));            }              i = n;              while (i--)              {                  int j = 0;                  while (j < i)                  {                      g_nPointList[i][j] = abs(PointList[i].x, PointList[j].x) +                          abs(PointList[i].y, PointList[j].y);                      j++;                  }              }              i = n;             initailArray(max1 + 1, max2 + 1);             valuex = max1, valuey = max2;            printf("%d\n", DeepInto(i));          }      }      return 0;  }  



20 个点的数据的时候 要大概3S左右:
例如这组
20
1 2
1000 1
1000 2
3 5
8 11111
222 156
123 123
125 159
11111 12
2101 23
15687 1264
456 4561
121 2315
1 123
1 3
2 56
6 98
9000 12
6000 112
2 1111
求高手解答..代码基本无注释,不好意思..
我的思路:穷举搜索的话 次数超多死T,T的原因应该是 很多组数据时重复在做的,所以,我用一个 二维数组保存做过的 组合,减少重复量,不过我能做的也就这么多....还是不行,20个点搜索 还是要 3S 多...



[解决办法]
是一个动态规划算法:

输入点对 P1, P2, P3, ... , Pn
点对的和表示为: distance(i,j)

1,最优子结构显而易见,从最优解中任意剔除一对点,得到的点对也是剩余的点的最优解。

2,递归函数:


递归函数S(len, i, j)定义为:从输入中获取len对点对 ,且所有点对中不包含点Pi,Pj时,可以找到的所有的len对点对中的最小长度和。 1 <= len <= n / 2, 1 <= i, j <= n;

E(len,i,j,k),表示某个最优解为S(len, i, j)的子问题中是否含有Pk,用0代表S(len, i, j)包含Pk,

S(len, i, j, k) = min { S(len - 1, i, j) + distance(k, l) : E(len - 1, i, j, k)和E(len - 1, i, j, l)等于1(即点Pk,Pl,不在S(len - 1, i, j)的最优解中 }

因为最优子结构特性,E(len - 1, i, j, k)的取值除增加一个新的点对外,其余取值与E(len, i, j, k)相同,那么自底向上的计算S(len, i, j)时,可以把一系列的E(len, i, j, k),合并成一个E(i, j,k),当递归计算在len时,E(i,j,k)即代表E(len - 1, i, j, k)。



贴下代码:
C/C++ code
#include <iostream>using namespace std;class Point {        friend class MinPairs;        int x, y;public:        Point(int x_ = 0, int y_ = 0) : x(x_), y(y_) { };        Point(const Point &p) { x = p.x; y = p.y; };        const Point &operator= (const Point &p) {                if (this == &p)                        return *this;                x = p.x;                y = p.y;                return  *this;        }        int get_x () { return x; }        int get_y () { return y; }};class MinPairs {        Point *points;        const int length;public:        MinPairs(const Point points_[], int len) : length(len) {                points = new Point[length];                cout << "points are: " << endl;                for (int i = 0; i != length; i++) {                        points[i] = points_[i];                        cout << i << ", (" << points[i].x << ", ";                        cout << points[i].y << ")" << endl;                }        }        int minimul_sum_of_pairs();private:        int distance(const Point &p1, const Point &p2) {                return abs(p1.x - p2.x) + abs(p1.y - p2.y);        }        int abs(int n) { return n < 0 ? -n : n; }};int MinPairs::minimul_sum_of_pairs(){        int S[length / 2 + 1][length][length];        unsigned char E[length][length][length];        int min, min_i, min_j;        for (int i = 0; i != length; i++)                for (int j = 0; j != length; j++)                        S[0][i][j] = 0;        for (int i = 0; i != length; i++)                for (int j = 0; j != length; j++)                        for (int k = 0; k != length; k++)                                E[i][j][k] = 1;        for (int pair_len = 1; pair_len != length / 2; pair_len++) {                for (int i = 0; i != length; i++) {                        for (int j = i + 1; j != length; j++) {                                min = 2147483647;                                for (int k = 0; k != length; k++) {                                        for (int l = k + 1; l != length; l++) {                                                if (k == i || k == j                                                    || l == i || l == j)                                                        continue;                                                if (E[i][j][k] == 0                                                    || E[i][j][l] == 0)                                                        continue;                                                int m = S[pair_len - 1][i][j] + distance(points[k], points[l]);                                                if (min > m) {                                                        min = m;                                                        min_i = k;                                                        min_j = l;                                                }                                        }                                }                                E[i][j][min_i] = E[i][j][min_j] = 0;                                S[pair_len][i][j] = S[pair_len][j][i] = min;                                cout << "pair_len " << pair_len << " without ";                                cout << "(" << i << "(" << points[i].x << "," << points[i].y <<"),";                                cout << j << "(" << points[j].x << "," << points[j].y << ")";                                cout << "has minimul sum : " << min << endl;                        }                }        }        /* 到这里,得到了所有只差一个点对的系列子问题, S( length / 2 - 1, i, j),对所有点对(i, j)判断,就可得到最优解 */        min = 2147483647;        for (int i = 0; i != length; i++) {                for (int j = i + 1; j != length; j++) {                        if (i == j) continue;                        if (min > S[length / 2 - 1][i][j] + distance(points[i], points[j])) {                                min = S[length / 2 - 1][i][j] + distance(points[i], points[j]);                                min_i = i;                                min_j = j;                        }                }        }        return min;}int main(){        Point points[] = {Point(1,9), Point(3,5), Point(5,30), Point(0, 0),                          Point(23,8), Point(24,12), Point(17,34),Point(15,50),                          Point(45,278),Point(35,89), Point(170,243),Point(48,72),                          Point(62,39), Point(2,4), Point(5,7), Point(82,26),                          Point(22,93), Point(70,1), Point(40,9), Point(40,35)                         };        MinPairs mp(points, sizeof(points) / sizeof(Point));        cout << "minumul : " << mp.minimul_sum_of_pairs() << endl;} 


[解决办法]
除了57、58楼的办法(主要是匈牙利法)外……

还尝试了C(19,9)*P(10,10)的枚举(计算了很长的时间才算完)……
算出的结果也是19383……

【程序(纯枚举/验证用)】

C/C++ code
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <windows.h>struct node{    int x,y;}list[20];int map[20][20];int X[10];int Y[10];int t;int p[10];int c[10];int tmp[10];int savex[10];int savey[10];int ruler[20];int km(){    int i,j,k,flg;    int a,b;    memset(p,0,sizeof(int)*t);    memset(c,0,sizeof(int)*t);    memcpy(savex,X,sizeof(int)*t);        k=0;flg=1;    a=-1;    while(flg){        for(p[k]=0;p[k]<t;p[k]++){            for(i=0;i<k;i++){                if(p[i]==p[k]) break;            }            if(i!=k) continue;            c[k]++;            if(k==t-1){                ///////////////////////////////////////                b=0;                for(i=0;i<t;i++) b+=map[X[i]][Y[p[i]]];                if(a==-1||b<a){                    a=b;                    for(i=0;i<t;i++) savey[i]=Y[p[i]];                }                ///////////////////////////////////////                for(i=t-1,j=1;c[i]==j&&i>=0;i--,j++);                if(j==t+1){                    flg=0;break;                }                k=i;                if(k<t-1){                    memset(c+k+1,0,sizeof(int)*(t-k-1));                }            }            else k++;        }    }    return a;}void fun(int n){    int i,j,k,a,b;    int sum;    int tm;    int x,flg;    if(n==2){        printf("%d\n",abs(list[0].x-list[1].x)+abs(list[0].y-list[1].y));        return;    }    memset(map,0,sizeof(int)*20*20);    for(i=0;i<n;i++){        for(j=i+1;j<n;j++){            a=abs(list[i].x-list[j].x)+abs(list[i].y-list[j].y);            map[i][j]=a;            map[j][i]=a;        }    }    if(n==4){        a=map[0][1]+map[2][3];        b=map[0][2]+map[1][3];        if(b<a) a=b;        b=map[0][3]+map[2][4];        if(b<a) a=b;        printf("%d\n",a);        return;    }    t=n/2;    sum=0;    //for(i=0;i<n;i=i+2) sum+=map[i][i+1];    for(i=0;i<t;i++) X[i]=i;    tm=0;    sum=19383;//sl point1    while(1){        GetTickCount();        i=0;j=1;k=1;        flg=1;        while(i<t){            if(k==X[j]&&j<t){                k++;j++;            }else{                Y[i]=k;i++;k++;            }        }        tm++;        if(tm>=76470){//sl point2            if(tm==80000) break;//sl point3            if(tm%10==0){                printf(".");                if(tm%100==0) printf("\ntm: [%d]\n",tm);                fflush(stdout);            }            a=km();            if(a<sum){                printf("\n");                for(x=0;x<t;x++){                    printf("%5d\t(%5d,%5d)(%5d,%5d)\n",map[savex[x]][savey[x]],list[savex[x]].x,list[savex[x]].y,list[savey[x]].x,list[savey[x]].y);                }                sum=a;printf("[%d] sum=%d\n",tm,sum);                fflush(stdout);            }        }        if(X[t-1]==n-1){            for(i=t-2,j=n-2;i>=1;i--,j--) if(X[i]!=j) break;            if(i==0) break;            for(j=X[i]+1;i<t;i++,j++) X[i]=j;        }else X[t-1]++;    }    printf("\nsum: %d\n",sum);}int main(){    int i,c,n;    int x,y;    scanf("%d",&c);    while(c--){        scanf("%d",&n);        for(i=0;i<n;i++){            scanf("%d %d",&x,&y);            list[i].x=x;            list[i].y=y;        }        fun(n);    }    return 0;}
[解决办法]
还是没想全面,原来的实现由所有子问题在O(n^2)内解出新问题,原来不全面的思路基于子问题和新问题是否“交叉”,分别求出两种情况下的最小值,但这不全面。

求一新问题S(len,i,j)的最小值时,假定新问题包括某一点对(k,l),考虑下面两种情况:
1,如果S(len-1,k,l)不包括i和j,那么如果包含(k,l)的S(len,i,j)最小值肯定是等于S(len-1,k,l)+d(k,l),因为S(len-1,k,l)的意思就是长度为len-1且不包括(k,l)的子问题最优解。如此,列出所有(k,l)的情况。



2,如果S(len-1,k,l)包含了i,j,这时不应该舍弃(k,l),而是应该在长度为len-1的所有(n^2/2-n)个子问题S(len-1,x,y)中选择一个不包含(k,l)和(i,j)的子问题S(len-1,sub_i,sub_j),由S(len-1,sub_i,sub_j)+d(k,l)构成使。

S(len,i,j)最终取值为:所有(k,l)点对假设中得到的一系列S(len,i,j)的最小值,
从for循环上看,时间复杂度是:O(n^7),
直觉上,S数组代表共有n^3/2个子问题,每个子问题由(n^2/2-n)个(k,l)假设计算得到,每个(k,l)假设)要考虑所有len-1的(n^2/2 - n)个子问题,所以t = O(n^7),
这就相当慢了,也只能算算20个点。


希望现在的代码没有问题!!!!!!

C/C++ code
int MinPairs::minimum_sum_of_pairs(){        int px[length / 2 + 1][length][length];        int py[length / 2 + 1][length][length];        int S[length / 2 + 1][length][length];        unsigned char E[length/2+1][length][length][length];        int m, min, sub_i, sub_j, new_i, new_j;        for (int i = 0; i != length; i++)                for (int j = 0; j != length; j++)                        S[0][i][j] = 0;        for (int i = 0; i != length; i++)                for (int j = 0; j != length; j++)                        for (int k = 0; k != length; k++)                                E[0][i][j][k] = 1;        for (int pair_len = 1; pair_len != length / 2; pair_len++) {                for (int i = 0; i != length; i++) {                        for (int j = i + 1; j != length; j++) {                                min = 2147483647;                                int sub_i, sub_j, new_i, new_j;                                for (int k = 0; k != length; k++) {                                        if (k == i || k == j)                                                continue;                                        for (int l = k + 1; l != length; l++) {                                                if (l == i || l == j)                                                         continue;                                                if (E[pair_len - 1][k][l][i] == 0 || E[pair_len - 1][k][l][j] == 0) {                                                    for (int si = 0; si != length; si++)                                                        for (int sj = si + 1; sj != length; sj++)                                                                if (E[pair_len - 1][si][sj][i] == 1 && E[pair_len - 1][si][sj][j] == 1                                                                    && E[pair_len - 1][si][sj][k] == 1 && E[pair_len - 1][si][sj][l] == 1) {                                                                        if (min > S[pair_len - 1][si][sj] + d(k, l)) {                                                                                min = S[pair_len - 1][si][sj] + d(k, l);                                                                                sub_i = si; sub_j = sj;                                                                                new_i = k; new_j = l;                                                                        }                                                                }                                                } else {                                                        if (min > S[pair_len - 1][k][l] + d(k, l)) {                                                                min = S[pair_len - 1][k][l] + d(k, l);                                                                sub_i = k; sub_j = l;                                                                 new_i = k; new_j = l;                                                        }                                                }                                        }                                }                                S[pair_len][i][j] = min;                                px[pair_len][i][j] = new_i;                                py[pair_len][i][j] = new_j;                                for (int l = pair_len - 1; l != 0; l--) {                                        px[l][i][j] = px[l][sub_i][sub_j];                                        py[l][i][j] = py[l][sub_i][sub_j];                                }                                for (int k = 0; k != length; k++)                                        E[pair_len][i][j][k] = E[pair_len - 1][sub_i][sub_j][k];                                E[pair_len][i][j][new_i] = E[pair_len][i][j][new_j] = 0;                                //cout << "pair_len " << pair_len << " without ";                                //cout << points[i] << "," << points[j];                                //cout << " has minimum sum : " << min << endl;                        }                }        }        // 到这里,得到了所有只差一个点对的系列子问题, S( length / 2 - 1, i, j),        // 对所有点对(i, j)判断,就可得到最优解        min = 2147483647;        for (int i = 0; i != length; i++) {                for (int j = i + 1; j != length; j++) {                        if (min > S[length / 2 - 1][i][j] + distance(points[i], points[j])) {                                min = S[length / 2 - 1][i][j] + distance(points[i], points[j]);                                sub_i = i;                                sub_j = j;                        }                }        }                // 从 px, py中输出最优点对         cout << "pairs:" << endl;        cout << points[sub_i] << ", \t" << points[sub_j] << endl;        for (int len = length / 2 - 1; len != 0; len--) {                new_i = px[len][sub_i][sub_j];                new_j = py[len][sub_i][sub_j];                cout << points[new_i] << ", \t" << points[new_j] << endl;        }                return min;} 


[解决办法]
LZ试试这样的数据结构,先对所有的点按照x坐标排个序,逐一加入树结构,
用第一个点作为根节点,第2个点作为叶子节点,加入第3个节点P3,

如果p3到p1的距离D[1,3] < D[2,3],再比较D[1,2]和D[2,3],如果D[1,2] < D[2,3]
则说明p1在p2,p3之间,则将节点顺序调整为p3,p1,p2
如果D[1,2] < D[2,3]则说明p3在p1,p2之间,则将节点顺序调整为p1,p3,p2

如果p3到p1的距离D[1,3] > D[2,3],进行同样类似的比较,调整节点顺序。

每次加入一个新的节点,就不断按照上面的方法调整节点顺序,直到最后一个节点加入
这样一个完整的链表就形成了。按照顺序取点应该就可以了!

读书人网 >软件架构设计

热点推荐