codeforces 8c Looking for Order

https://vjudge.net/problem/CodeForces-8C

题意:

一个平面上放着许多东西,每个东西都有一个坐标,最开始一个人在一个起始坐标,她出发去拿东西,一次要么拿一件东西,要么拿两件东西,拿了之后必须返回起始坐标。

每次花费的时间是两个坐标距离的平方,问拿完所有的东西需要的最少的时间。

思路:

由于数据范围比较小,所以可以考虑用状压dp来写。由于每次拿东西之后都要返回起点,那么其实拿东西的顺序是没有影响的,所以利用题目给定的顺序进行剪枝,即每次进行扩展的时候都考虑在前面的点已经取完了。

然后每次记录的时候,非常巧妙的方法,如果一个点的话,直接记录这个点,如果有两个点的话,那么就用(i+1)*100 + (j+1)来记录,因为点数最多时只有24,输出的时候递归输出就行了,递归输出这里还是比较巧妙的。

代码:(有详细的注释)

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <vector>
  4 using namespace std;
  5 
  6 struct node
  7 {
  8     int x,y;
  9 } a[30];
 10 
 11 int mp[30][30];
 12 vector<int> ans;
 13 
 14 int cal(int i,int j)
 15 {
 16     return (a[i].x - a[j].x) * (a[i].x - a[j].x) + (a[i].y - a[j].y) * (a[i].y - a[j].y);
 17 }
 18 
 19 const int maxn = (1 << 24)+2;
 20 const int gg = 0x3f3f3f3f;
 21 
 22 int dp[maxn],last[maxn],rec[maxn];
 23 
 24 void output(int s)
 25 {
 26     if (~last[s])
 27         output(last[s]);//递归输出
 28 
 29     if (rec[s])
 30     {
 31         if (rec[s] > 100) ans.push_back(rec[s] / 100);//第一个扩展的点
 32 
 33         ans.push_back(rec[s] % 100);
 34         ans.push_back(0);//每次都要返回起点
 35     }
 36 }
 37 
 38 int main()
 39 {
 40     int n;
 41 
 42     node tmp;
 43 
 44     scanf("%d%d",&tmp.x,&tmp.y);
 45 
 46     scanf("%d",&n);
 47 
 48     for (int i = 0;i < n;i++)
 49     {
 50         scanf("%d%d",&a[i].x,&a[i].y);
 51     }
 52 
 53     a[n] = tmp;
 54 
 55     for (int i = 0;i <= n;i++)
 56         for (int j = i + 1;j <= n;j++)
 57         mp[i][j] = mp[j][i] = cal(i,j);
 58 
 59     memset(dp,gg,sizeof(dp));
 60     memset(last,-1,sizeof(last));
 61 
 62     dp[0] = 0;
 63 
 64     for (int s = 0;s < (1<<n);s++)
 65     {
 66         if (dp[s] >= gg) continue;
 67 
 68         for (int i = 0;i < n;i++)
 69         {
 70             if (((1 << i) & s) == 0)//这个点没有被走过
 71             {
 72                 int val = dp[s] + mp[n][i] * 2;
 73 
 74                 if (dp[s|(1 << i)] > val)
 75                 {
 76                     dp[s|(1 << i)] = val;
 77                     last[s|(1 << i)] = s;//记录前驱,下同
 78                     rec[s|(1 << i)] = i+1;
 79                 }
 80 
 81                 for (int j = i + 1;j < n;j++)//从i+1开始枚举保证了不会有重复情况
 82                 {
 83                     if (((1<<j)&s) == 0)
 84                     {
 85                         int tmp = dp[s] + mp[n][i] + mp[i][j] + mp[j][n];
 86 
 87                         if (dp[s|(1<<i)|(1<<j)] > tmp)
 88                         {
 89                             dp[s|(1<<i)|(1<<j)] = tmp;
 90                             last[s|(1<<i)|(1<<j)] = s;
 91                             rec[s|(1<<i)|(1<<j)] = (i+1) * 100 + (j+1);//巧妙的记录
 92                         }
 93                     }
 94                 }
 95 
 96                 break;//强行顺序剪枝
 97             }
 98         }
 99 
100         //printf("%d\n",dp[s]);
101     }
102 
103 
104     ans.clear();
105 
106     output((1<<n)-1);
107 
108     printf("%d\n",dp[(1<<n)-1]);
109 
110     printf("0 ");
111 
112     for (int i = 0;i < ans.size();i++)
113         printf("%d%s",ans[i],i == ans.size() - 1 ? "\n" :" ");
114 
115     return 0;
116 }

 

posted @ 2017-10-07 23:39  qrfkickit  阅读(461)  评论(0编辑  收藏  举报