POJ 2288 Islands and Bridges

本文深入探讨了一道复杂路径问题的求解策略,通过状态压缩和搜索技术,作者成功地优化了解题过程。文章详细阐述了题目的核心概念、解题思路以及实现细节,特别强调了如何通过合理设计状态压缩数组来提高算法效率。同时,作者还分享了状态压缩过程中遇到的挑战以及解决方案,为读者提供了一套完整的解题指南。

     感想:好像很多人都在刷这题啊,我来详细的写写吧。

     题意:(话说这题的题意还算简单)给你一些岛,然后一些连接岛的桥(无向的),求路径的价值有3种方式,求Hamilton path(过所有点一遍,不经过重复的点),这条路径有个价值,该价值由3部分组成,①加上岛的权值    ②加上连续2岛之间边的权值(2岛权值的乘积)  ③经过连续的3个岛,CiCi+1Ci+2,如果Ci、Ci+2之间有桥进行连接,加上(Vi*Vi+1*Vi+2)。求路径最大价值,及拥有最大价值路径的数目,正向反向算是一种,所以最后需要除以2。

    思路:这13个岛,可能很多人都已经想到状态压缩和搜索,本人模拟了下,发现搜索需要考虑的东西太多,于是直接就想状态压缩。其实这题的状态压缩的思路很好想,dp数组设成3维的,1维表示如今的状态,后2维表示最后走的2路(因为只有最后的2步才对后序的情况构成影响)。但是就是这个路径的价值的最大值和最大值路径数目这2个值的存储 把我弄昏了,还是怪自己太古板,看了看别人的博客,基本思路一样,就是加了个num数组来记录最大值路径的数目。这里还需要注意的便是   你在dp时,可以将状态(也就是dp的第一维)设为主序,因为往后面走肯定状态值要大于前面已走的状态值。

<pre name="code" class="cpp">#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
LL n, m, val[14], dp[1<<13][14][14], num[1<<13][14][14];
bool map[14][14];//记录边

bool ok(int i,int j)//判断j点是否在此路径中
{
    if(j==0)
        return true;
    if(i&(1<<(j-1)))
        return true;

    return false;
}

void init() {
      memset(dp, -1, sizeof(dp));
      memset(map, false, sizeof(map));
      memset(num, 0, sizeof(num));
}

int main(void)
{
    int ncase;
    cin>>ncase;
    while(ncase--)
    {
        init();
      scanf("%d %d", &n, &m);
      for(int i=1; i<=n; ++i)
       scanf("%d", val + i);
      for(int i = 1; i <= m; ++i)
      {
          int a, b;
          scanf("%d %d", &a, &b);
          map[a][b] = map[b][a] = true;
      }
      if(n == 1)
      {
        cout<<val[1]<<" "<<1<<endl;
        continue;
      }
      else
      {
        for(int i=1; i<=n; ++i)
        {
            int j=0;
            j |= (1 << (i-1));
            dp[j][i][0] = val[i];
            num[j][i][0] = 1;
        }
        for(int i = 0; i < (1<<n); ++i)
        {
          for(int j = 1; j <= n; ++j)//j点表示即将要走的那个点
          {
            if(!ok(i, j))
                continue;
            int tmp = i^(1<<(j-1));
            for(int k = 1; k <= n; ++k)
            {
                if(k == j)
                continue;

                if(!ok(i,k))
                 continue;

                if(!map[k][j])
                continue;

                for(int q = 0; q <= n; ++q)
                {
                   if(q == k || q == j)
                    continue;//即将走的下一个点

                   if(!map[q][k] && q)
                   continue;

                   if(!ok(tmp, q))
                   continue;

                   if(dp[tmp][k][q] == -1)
                    continue;
                   int tmp1 = val[j] + val[j] * val[k] + dp[tmp][k][q];
                   if(map[q][j])
                   tmp1 += val[q] * val[j] * val[k];

                   if(tmp1 == dp[i][j][k])
                     num[i][j][k] += num[tmp][k][q];
                   if(tmp1 > dp[i][j][k])
                   {
                       dp[i][j][k] = tmp1;
                       num[i][j][k] = num[tmp][k][q];
                   }
                }
            }
          }
        }
      LL ans1=-1, ans2=0;
      for(int s= (1<<n) - 1, i = 1; i <= n; i++)
      {
        for(int j = 1; j <= n; j++)
         {
             if(i==j)
                continue;
             if(ans1 == dp[s][i][j])
              ans2 += num[s][i][j];
             if(dp[s][i][j] > ans1)
             {
                 ans1 = dp[s][i][j];
                 ans2 = num[s][i][j];
             }
         }
      }
      if(ans1 == -1)
        printf("0 0\n");
      else
        cout<<ans1<<" "<<ans2/2<<endl;
      }
    }

    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值