zoukankan      html  css  js  c++  java
  • 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
    ",dp[s]);
    101     }
    102 
    103 
    104     ans.clear();
    105 
    106     output((1<<n)-1);
    107 
    108     printf("%d
    ",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 ? "
    " :" ");
    114 
    115     return 0;
    116 }
  • 相关阅读:
    【php】PHP检测json格式数据
    【php】PHP那些非常有用却鲜有人知的函数
    MySQL DELETE 语句:语法及案例剖析、从命令行中删除数据
    MySQL UPDATE 更新:语法及案例剖析
    MySQL WHERE 子句:语法及案例剖析、从命令提示符中读取数据
    mysql实现主从复制/主从同步
    docker安装mysql方法
    MySQL之账户管理的几种方式
    MySQL 查询数据:语法及案例剖析
    MySQL 插入数据:语法以及案例剖析
  • 原文地址:https://www.cnblogs.com/kickit/p/7636236.html
Copyright © 2011-2022 走看看