• DP:Islands and Bridges(POJ 2288)


                    2015-09-21

                    

                    造桥基建工程

      题目大意,就是有n座岛和k座桥,要你找一条哈密顿圈(找完所有的岛,并且每个岛只经过一次),当经过一座岛就加上岛的价值,如果两岛联通,则加上两座岛的价值之积,如果三座岛之间构成三角联通,则再加上三岛之积,问最大价值的哈密顿圈和最大价值和哈密顿圈的个数

      哈密顿圈是是一个NP完全的问题,用DP就可以解决这个问题,现在的问题就是,怎么解决呢?

      首先我们要明确,这一题要用DP做什么,首先这一题的最后肯定要求到最后岛全部都通过的情况,然后还需要保留前两个岛的信息

      那么这个时候我们可以想到用状态压缩去做(因为这里岛只能经过一次,而且经过以后状态都是等效的)101111,就表示第五个岛还没被经过,本来呢这个状态压缩应该是保留状态的,但是这样一来我们就需要保留1<<n个*3个维度,那么这一题我们可以放弃了,因为内存肯定不够,所以我们要想着优化储存空间,由于我们只用担心前两个岛是什么,而不是岛的所在状态(反正表示也是1001111)这样的,所以我们可以把两个岛的维度改为n*n,那么最后dp就是一个[1<<n][n][n]的三维数组

      具体而言,对于不合法的位置,我们老方法,要定义其为-1,同时还要判断图是否联通

      我们先从i=1(0000....0001)开始枚举,表示岛已经经过的位置,如果我们需要上一次还没来到这个岛的位置的状态,我们使用i^(1<<(j-1))这个方法,并且判断第j个岛的前两座岛是否有冲突。

      如果算出来的dp值比dp[i][j][k]要大,则更新,并且num是继承dp[state_old][k][q]的状态,如果想等,则使num[i][j][k]+=num[state_old][k][q](表示还有更多的路与此状态相关)。

      最后基准状态,q=0的时候可以放入岛的价值,那么最后就可以一起处理了,并且只有一个岛的时候,要单独处理,并且数据类型要是long long

      参考:http://blog.csdn.net/lenleaves/article/details/7981788

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 static int islands_value[14];
      6 int map[14][14];
      7 long long dp[1 << 13][14][14];//因为要保存两个以上状态,所以必须是三维数组,前面一位是状态数
      8 long long num[1 << 13][14][14];
      9 
     10 void Search(const int);
     11 static int If_Valid(const int, const int);
     12 
     13 int main(void)
     14 {
     15     int sum_islands, sum_paths, sum_obj, i, j, k, x, y;
     16     
     17     scanf("%d", &sum_obj);
     18     for (i = 0; i < sum_obj; i++)//输入
     19     {
     20         scanf("%d%d", &sum_islands, &sum_paths);
     21         for (j = 1; j <= sum_islands; j++)
     22             scanf("%d", &islands_value[j]);
     23         memset(map, 0, sizeof(map));
     24         memset(dp, -1, sizeof(dp));
     25         memset(num, 0, sizeof(num));
     26         for (k = 0; k < sum_paths; k++)
     27         {
     28             scanf("%d%d", &x, &y);
     29             map[x][y] = map[y][x] = 1;//读图1
     30         }
     31         Search(sum_islands);
     32     }
     33     return 0;
     34 }
     35 
     36 static int If_Valid(const int x, const int y)
     37 {
     38     /*If_Value函数:
     39      功能:判断y位置是否被访问过
     40      返回值:被访问过返回1,未被访问放回0,如果y是0,则就是基准情况,一定要给通过
     41     */
     42     if (y == 0) return 1;
     43     if (x&(1 << (y - 1))) return 1;
     44     else return 0;
     45 }
     46 
     47 void Search(const int sum_islands)
     48 {
     49     int i, j, k, q, state_old;
     50     long long tmp, ans1, ans2;
     51 
     52     if (sum_islands == 1)
     53     {
     54         printf("%d 1
    ", islands_value[1]);
     55         return;
     56     }
     57     for (j = 1; j <= sum_islands; j++)
     58     {
     59         dp[1 << (j - 1)][j][0] = islands_value[j];
     60         num[1 << (j - 1)][j][0] = 1;//初始化的时候价值为1
     61     }
     62     for (i = 1; i < (1 << sum_islands);i++)
     63     {
     64         for (j = 1; j <= sum_islands; j++)
     65         {
     66             state_old = i ^ (1 << (j - 1));//得到当j还没有出现时候的位置
     67             if (If_Valid(i, j))//这个位置就是要考虑的位置
     68             {
     69                 for (k = 1; k <= sum_islands; k++)
     70                 {
     71                     if (k != j && map[j][k] && If_Valid(i, k))
     72                     {
     73                         for (q = 0; q <= sum_islands; q++)
     74                         {
     75                             if (!map[k][q] && q) continue;//q = 0的时候是没有意义的
     76                             if (q != j&&q != k
     77                                 && If_Valid(state_old, q)//是否是需要的q
     78                                 && dp[state_old][k][q] != -1)//这个位置一定要有意义
     79                             {
     80                                 tmp = islands_value[j] + islands_value[j] * islands_value[k] + dp[state_old][k][q];
     81                                 if (map[j][q])
     82                                     //假设j和q是联通的
     83                                     tmp += islands_value[j] * islands_value[k] * islands_value[q];
     84                                 if (tmp == dp[i][j][k])
     85                                     //如果得到的结果是和ijk这个位置已经重复了,则自增(中间状态)
     86                                     num[i][j][k] += num[state_old][k][q];
     87                                 if (tmp > dp[i][j][k])
     88                                     //如果比ijk这个状态还要大,则更新dp,继承num
     89                                 {
     90                                     dp[i][j][k] = tmp;
     91                                     num[i][j][k] = num[state_old][k][q];
     92                                 }
     93                             }
     94                         }
     95                     }
     96                 }
     97             }
     98         }
     99     }
    100     for (ans1 = -1, ans2 = 0, j = 1; j <= sum_islands; j++)
    101     {
    102         for (k = 1; k <= sum_islands; k++)
    103         {
    104             if (k != j)
    105             {
    106                 if (ans1 == dp[(1 << sum_islands) - 1][j][k])
    107                     ans2 += num[(1 << sum_islands) - 1][j][k];
    108                 if (ans1 < dp[(1 << sum_islands) - 1][j][k])
    109                 {
    110                     ans1 = dp[(1 << sum_islands) - 1][j][k];
    111                     ans2 = num[(1 << sum_islands) - 1][j][k];
    112                 }
    113             }
    114         }
    115     }
    116     if (ans1 == -1)
    117         printf("0 0
    ");
    118     else
    119         printf("%lld %lld
    ", ans1, ans2 / 2);
    120 }
  • 相关阅读:
    清理计算机硬盘
    DIY-组装
    go函数类型的使用
    go同步互斥锁
    Go读写文件
    go mod
    go html
    channel
    arp和rarp协议
    自己实现的反射
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/4824920.html
Copyright © 2020-2023  润新知