hoj 2473 Fish Can Fly解题报告

http://acm.hit.edu.cn/judge/show.php?Proid=2473&Contestid=0

一道很好的状态压缩的DP,题目要求在一定时间范围内从起点走到终点,使途径的点上有权值尽量的大,如果按照一遍TSP的思路的话,是不可行的,例如样例1,如果从起点,沿着路径B->C,得到C点B点的权值,如果C点无法到达终点,B可以到达终点,则要回到B点。但是这样该曾状态的的循环已经结束了,无法完成要求。那么如何满足这样的转移呢?

首先我在TSP每层状态枚举起点的时候枚举N次,这样可能处理从当前状态向回走并转移的问题,但是复杂度太大,所以不可行,那么需要跟好的DP进行优化,先做一次floyd 直接找到从起点到C的最短时间,直接从C转移回B。

关于正确性,如果B在起点到C的最短路上,那么最后总所得的权值一样是B + C的权值。于是会产生这样的问题,如果有路径起点->A->B->C 那么我们得到C的最短路,并用之直接转移是否有状态ABC呢?因为ABC是一条floyd处理出来的最短路,所以一定有最短路AC = ABC,那么从A->C->B的时间等于从A->B->C->B。这样我们即得到了最短路又得到了相应的状态而不用担心向回走的问题了,于是就转化为一般的TSP问题DP解法。

代码如下

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int SIZE = 13;
const int INF = 1000000000;
int maps[SIZE][SIZE], dp[SIZE][1 << SIZE], score[SIZE] = {0};
int n, t, lmt;

 

void DP()
{
    for (int i = 0; i < n; i++)
        for (int j = 0; j < (1 << n); j++)
            dp[i][j] = INF;
    dp[0][1] = 0;
    for (int i = 1; i < (1 << n); i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (!(i & (1 << j)) || dp[j][i] == INF)
                continue;
            for (int k = 0; k < n; k++)
            {
                dp[k][i | (1 << k)] = min(dp[k][i | (1 << k)], dp[j][i] + maps[j][k]);
            }
        }
    }
    int ans = -1;
    for (int j = 1; j < (1 << n); j++)
    {
        if (!(j & (1 << (n - 1))) || dp[n - 1][j] > lmt)
            continue;
        int tmpans = 0, tmp = j, pt = 0;
        while (tmp)
        {
            if (tmp & 1)
                tmpans += score[pt];
            pt++;
            tmp >>= 1;
        }
        ans = max(ans, tmpans);
    }
    if (ans == -1)
        printf("Miss bailey!\n");
    else
        printf("Fish can fly! %d\n", ans);
}
void floyd()
{
    for(int k = 0;k < n;k++)
    {
        for(int i = 0;i < n;i++)
        {
            for(int j = 0;j < n;j++)
                if(maps[i][k] && maps[k][j])
                    maps[i][j] = min(maps[i][j],maps[i][k] + maps[k][j]);
        }
    }
}
int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        n += 2;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                scanf("%d", &maps[i][j]);
                if (maps[i][j] == 0)
                    maps[i][j] = INF;
            }
        }
        memset(score, 0, sizeof (score));
        for (int i = 1; i <= n - 2; i++)
            scanf("%d", &score[i]);
        scanf("%d", &lmt);
        floyd();
        DP();
    }
    return 0;
}

你可能感兴趣的:(sh)