Poj P2288 Islands and Bridges___壮压dp

题目大意:

给出 Q 组数据,
每组数据给出 N 个点的带权无向图,点从 0 N 1 标号,求起点 0 到起终点 N 1 的最短 H a m i l t o n 路径。
对于路径权值的计算:
1.经过所有点的权值相加。
2.经过的连续两个点的权值的乘积。
3.能够直接满足 a > b > c > a 的连续三个点的乘积。
问最大权值和这个最大权值的路线有多少条。

1 Q 20
N 13

分析:

不难想到可以壮压 d p

f [ k ] [ i ] [ j ] 表示状态为 k ,最后一个到达的点为 i ,倒数第二个到达的点为 j 的最短 H a m i l t o n 路径。
n u m [ k ] [ i ] [ j ] 表示状态为 k ,最后一个到达的点为 i ,倒数第二个到达的点为 j 时, H a m i l t o n 路径大小为 f [ k ] [ i ] [ j ] 的数量。
则有
f [ k ] [ i ] [ j ] =max{ f [ k x o r ( 1 << i ) ] [ j ] [ k ] + a [ i ] + a [ i ] a [ j ] + ( f l a g [ i ] [ k ] a [ i ] a [ j ] a [ k ] : 0 ) }
f l a g [ i ] [ j ] 表示 i , j 的连通关系,
对于 n u m [ k ] [ i ] [ j ] ,在计算 f [ k ] [ i ] [ j ] 的时候顺带计算一下即可。

最后找一个max{ f [ 2 n 1 ] [ i ] [ j ] },设为 x
统计所有 f [ 2 n 1 ] [ i ] [ j ] = x n u m [ 2 n 1 ] [ i ] [ j ] 的总和

代码:


#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 14

using namespace std;

typedef long long ll;

ll num[1<<N][N][N], f[1<<N][N][N], a[N]; 
bool cp[N][N];

int main() 
{
    int T, n, m;
    scanf("%d", &T);
    while (T--) 
    {
           memset(num, 0, sizeof(num));
           memset(cp, 0, sizeof(cp));
           memset(f, 0, sizeof(f));
           scanf("%d %d", &n, &m);
           for (int i = 0; i < n; i++) scanf("%d", &a[i]);
           int u, v;
           for (int i = 0; i < m; i++) 
           {
                scanf("%d %d", &u, &v);
                if (u != v)
                {
                    cp[--u][--v] = cp[v][u] = 1;
                    int cnt = (1<<u)+(1<<v);
                    num[cnt][u][v] = num[cnt][v][u] = 1;
                    f[cnt][u][v] = f[cnt][v][u] = a[u] + a[v] + a[u]*a[v];
                }
            }
           if (n == 1) 
           {
               printf("%lld 1\n", a[0]);
               continue;
           }
           for (int l = 0; l < (1<<n); l++) 
                for (int i = 0; i < n; i++) 
                     if ((l>>i) & 1) 
                     {
                         for (int j = 0; j < n; j++) 
                              if (((l>>j) & 1) && i != j && cp[i][j])
                              {
                                  for (int k = 0; k < n; k++) 
                                       if (((l>>k) & 1) && i != k && j != k && cp[j][k]) 
                                       {
                                           int cnt = l xor (1<<i);
                                           if (!f[cnt][j][k]) continue;
                                           ll tot = f[cnt][j][k] + a[i] + a[i]*a[j] + (cp[i][k]?a[i]*a[j]*a[k]:0);
                                           if (tot > f[l][i][j]) 
                                           {
                                               f[l][i][j] = tot;
                                               num[l][i][j] = num[cnt][j][k];
                                           }
                                           else if (tot == f[l][i][j])
                                                {
                                                    num[l][i][j] += num[cnt][j][k];
                                                }
                                       }    
                              }
                    }
            ll ans = -1, tot = 0;
            for (int i = 0; i < n; i++) 
                 for (int j = 0; j < n; j++) 
                      if (i != j && cp[i][j])
                      {
                          if (f[(1<<n)-1][i][j] > ans) 
                          {
                              ans = f[(1<<n)-1][i][j];
                              tot = num[(1<<n)-1][i][j]; 
                          }  
                          else if (f[(1<<n)-1][i][j] == ans) 
                               {
                                  tot += num[(1<<n)-1][i][j];
                               }
                      }
            if (ans == -1) printf("0 0\n");
                      else printf("%lld %lld\n", ans, tot/2);

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/gx_man_vip/article/details/81304578
今日推荐