hdu3681Prison Break 状压dp TSP 二分

点击打开链接

题意:机器人逃狱,需把所有开关Y遍历才能成功,G可以使机器人充满电(只能使用一次,也可暂时不用),D不能走,S为空,走一步要消耗一格电,没电无法继续走,问机器人能逃出去起始需要具有的最少电量。

思路:把G,Y抽离出来建立一张图,预处理任意两点的最短距离(bfs),G和Y都只通过一次(这里并不意味着走到G就必须用掉,很明显由于是最短路径,用掉是最优解,有些G可能包含在最短路径之中通过而没有用),于是就是一个TSP问题,二分答案。

TSP:dp[s][i]表示已走节点为s,且最后走的是i,枚举已走节点j(j!=i),dp[s][i]=max(dp[s-j][k]+dis),k为s-j集合中已走节点。s可有小到大遍历,因为处理s时,s之前的状态都已经处理过。

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#define inf 10000000
#define pi acos(-1.0)
#define eps 1e-8
#define seed 131
using namespace std;
typedef pair<int,int> pii;
typedef pair<pii,int> pe;
typedef unsigned long long ULL;
typedef long long LL;
const int maxn=100005;
char g[16][16];
int n,m;
int dis[16][16][16][16];
int dir[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
struct Node
{
    int x,y;
}node[18];
int dp[18][1<<18];
int num;
int mid;
int ny,ng;
void bfs(int r0,int c0);
bool TSP(int c,int s);
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0)
            break;
        ny=0,ng=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                cin>>g[i][j];
                if(g[i][j]=='Y')
                    ny++;
                if(g[i][j]=='G')
                    ng++;
            }
        }
        num=ny+ng+1;
        int e1=1;
        int e2=0;
        memset(dis,-1,sizeof(dis));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(g[i][j]=='F'||g[i][j]=='G'||g[i][j]=='Y')
                {
                    if(g[i][j]=='F')
                    {
                        node[0].x=i;
                        node[0].y=j;
                    }
                    if(g[i][j]=='Y')
                    {
                        node[e1].x=i;
                        node[e1++].y=j;
                    }
                    if(g[i][j]=='G')
                    {
                        node[ny+1+e2].x=i;
                        node[ny+1+e2].y=j;
                        e2++;
                    }
                    bfs(i,j);
                }
            }
        }
        int l=0,r=300;
        bool flag=false;
        while(l<r)
        {
            mid=(l+r)/2;
            dp[0][1]=mid;
            if(TSP(0,1))
            {
                flag=true;
                r=mid;
            }
            else
                l=mid+1;
        }
        if(!flag)
            printf("-1\n");
        else
            printf("%d\n",l);
    }
    return 0;
}
bool ok(int s)
{
    for(int i=1;i<=ny;i++)
    {
        if(!((1<<i)&s))
            return false;
    }
    return true;
}
bool TSP(int c,int s)
{
    for(int i=s+1;i<(1<<num);i++)
    {
        for(int j=0;j<num;j++)
        {
            if(!((1<<j)&i))
                continue;
            dp[j][i]=-1;
            for(int k=0;k<num;k++)
            {
                if(k==j||(!((1<<k)&i))||dis[node[j].x][node[j].y][node[k].x][node[k].y]==-1)
                    continue;
                dp[j][i]=max(dp[j][i],dp[k][i^(1<<j)]-dis[node[j].x][node[j].y][node[k].x][node[k].y]);
                if(dp[j][i]<0)
                    continue;
                if(g[node[j].x][node[j].y]=='G')
                    dp[j][i]=mid;
                if(dp[j][i]>=0&&ok(i))
                    return true;
            }
        }
    }
    return false;
}
void bfs(int r0,int c0)
{
    bool vis[16][16];
    memset(vis,0,sizeof(vis));
    queue<pe>que;
    que.push(pe(pii(r0,c0),0));
    vis[r0][c0]=1;
    dis[r0][c0][r0][c0]=0;
    while(!que.empty())
    {
        pe p=que.front();
        que.pop();
        for(int i=0;i<4;i++)
        {
            int r=p.first.first+dir[i][0];
            int c=p.first.second+dir[i][1];
            if(r>=1&&r<=n&&c>=1&&c<=m&&vis[r][c]==0&&g[r][c]!='D')
            {
                if(g[r][c]=='F'||g[r][c]=='G'||g[r][c]=='Y')
                {
                    dis[r0][c0][r][c]=p.second+1;
                }
                vis[r][c]=1;
                que.push(pe(pii(r,c),p.second+1));
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值