HDU_3681 TSP问题,状压DP

<p>吐槽一下,最近事情真是多。。。大创+大挑+考试+实验报告(一个礼拜5个实验。。。)+各种抄作业+帮四省赛装电脑,然后一直没怎么做题。。。</p><p>然后再吐槽一下这道题,读题不细心真是惨啊。。。一开始看成了G点和Y点分别不超过15个,想想,加起来用30个点,我去。。。这怎么搞。。。拼命搞了两天没搞出来。。。两天以后又看了一下题目,发现是一共加起来不超过15个点(醉了,难怪我英语可能要挂科了),这就是一个普通的TSP问题了,不过并不是遍历所有的点一次,而是选定的点各一次,这个时候加一个末状态就好了。先建图,每个点做一次bfs就好了(找出最短路径),dp[s][i]表示第s状态最后到达i点的最大剩余体力。转移方程就是dp[s][j] = max(dp[pre_s][i]-dis[i][j]),这里要注意一下边界问题。。。对于TSP问题,我总觉的我的写法比较奇葩(但是都过了,以后如果出问题了再改吧)。最近感觉c++迭代器里面的东西确实好用。。。</p><p>下面直接给代码</p>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define ll  long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)

using namespace std;

typedef pair<int,int>   pii;

pii no[16];
int en,st,dis[16][16][16][16],n,N,M,dp[1<<16][16];
char mat[17][17];
int dx[4] = {-1,1,0,0},dy[4] = {0,0,-1,1};
void input(){
    n = 0;
    en = 0;
    FOR(i,0,N){
        scanf("%s",mat[i]);
        FOR(j,0,M){
            if(mat[i][j] == 'F'){
                st = n;
                en = en | (1<<n);
                no[n++] = make_pair(i,j);
                continue;
            }
            if(mat[i][j] == 'G'){
                no[n++] = make_pair(i,j);
                continue;
            }
            if(mat[i][j] == 'Y'){
                en = en | (1<<n);
                no[n++] = make_pair(i,j);
                continue;
            }
        }
    }
}

void bfs(pii um){
    queue <pii> q;
    dis[um.first][um.second][um.first][um.second] = 0;
    q.push(um);
    while(!q.empty()){
        pii u = q.front();  q.pop();
        int x = u.first,y = u.second;
        FOR(i,0,4){
            int nx = x + dx[i],ny = y + dy[i];
            if(nx < 0 || nx >= N || ny < 0 || ny >= M)   continue;
            if(mat[nx][ny] == 'D')  continue;
            if(dis[um.first][um.second][nx][ny] != -1)    continue;
            dis[um.first][um.second][nx][ny] = dis[um.first][um.second][x][y] + 1;
            q.push(make_pair(nx,ny));
        }
    }
}

bool init(){
    memset(dis,-1,sizeof(dis));
    FOR(i,0,n){
        bfs(no[i]);
    }
    FOR(i,0,n){
        if(mat[no[i].first][no[i].second] != 'G' && dis[no[st].first][no[st].second][no[i].first][no[i].second] == -1)
            return false;
    }
    return true;
}

bool check(int p){
    FOR(s,0,1<<n){
        FOR(i,0,n)
            dp[s][i] = -1;
    }
    dp[1<<st][st] = p;
    FOR(s,0,1<<n){
        if((s & (1<<st)) == 0)    continue;
        FOR(j,0,n){
            if((s & (1<<j)) == 0)   continue;
            if(j == st) continue;
            int pre_s = s ^ (1<<j);
            FOR(i,0,n){
                if((pre_s & (1<<i)) == 0) continue;
                int tem = dis[no[i].first][no[i].second][no[j].first][no[j].second];
                if(tem == -1)   continue;
                dp[s][j] = max(dp[pre_s][i]-tem,dp[s][j]);
            }
            if(dp[s][j] == -1)  continue;
            if((s & en) == en && dp[s][j] != -1)    return true;
            if(mat[no[j].first][no[j].second] == 'G')   dp[s][j] = p;
        }
    }
    return false;
}

int solve(){
    int l = 0,r = 1111111;
    check(2);
    while(l < r){
        int mid = (l+r)>>1;
        if(!check(mid)) l = mid+1;
        else r = mid;
    }
    return l;
}

int main(){
    //freopen("test.in","r",stdin);
    while(scanf("%d%d",&N,&M),(N || M)){
        input();
        if(en == (1<<st))   printf("0
");
        else if(!init())    printf("-1
");
        else    printf("%d
",solve());
    }
    return 0;
}



原文地址:https://www.cnblogs.com/hqwhqwhq/p/4555868.html