HDU 3681 Prison Break 越狱(状压DP,变形)

题意:

  给一个n*m的矩阵,每个格子中有一个大写字母,一个机器人从‘F’出发,拾取所有的开关‘Y’时便能够越狱,但是每走一格需要花费1点能量,部分格子为充电站‘G’,每个电站只能充1次电。而且部分格子为障碍'D'或者空格‘S’。机器人在开始时电池是满能量的,问电池容量至少为多少可以越狱?(1<=n,m<=14,充电站+开关的总个数<=15)

思路:

  看错题了,以为充电站和开关的个数分别至多为14,其实是两种加起来14。

  既然只有15个关键的格子,那么状压就有用了。假设每个点关键格必须走1次,那么就像可重复走的TSP了。但是充电站只能充1次,而且路过了也可以不充。所以,其实关键格只有那些“开关”,只要保证所有开关都收集全了,其他都无所谓。

  具体做法,求出这15个格子(包括起点)的两两之间的距离(BFS就够了),相当于一个完全连通图。再二分答案,判断是否能够收集所有开关。判断能否收集开关,就相当于求所有点只能走1次的欧拉路径了,直接状态压缩来求,注意:两点间不一定可达,遇到充电站必须充满电。这样子对于一个格子具体走过了多少格已经没有关系了。

  1 //#include <bits/stdc++.h>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <cmath>
  6 #include <deque>
  7 #include <map>
  8 #include <algorithm>
  9 #include <vector>
 10 #include <iostream>
 11 #define pii pair<int,int>
 12 #define INF 0x3f3f3f3f
 13 #define LL long long
 14 #define ULL unsigned long long
 15 using namespace std;
 16 const double PI  = acos(-1.0);
 17 const int N=16;
 18 int dp[1<<15][N], tag[N][N], vis[N][N], dis[N][N], n, m, egy;
 19 char g[N][N];
 20 
 21 struct node
 22 {
 23     int x,y,d;
 24     node(){};
 25     node(int x,int y,int d):x(x),y(y),d(d){};
 26 };
 27 inline bool istar(int x,int y){return tag[x][y]>0;}
 28 inline bool isok(int x,int y){ return (x>0&&x<=n)&&(y>0&&y<=m)&&g[x][y]!='D';}
 29 inline int cmp(node a,node b){ return  g[a.x][a.y]<g[b.x][b.y];}
 30 bool binary(int cap)
 31 {
 32     memset(dp,-1,sizeof(dp));
 33     dp[1][1]=cap;
 34     for(int s=1; s<(1<<n); s++)
 35     {
 36         for(int i=1; i<=n; i++)
 37         {
 38             if( (s&(1<<i-1))==0 )   continue;   //未走
 39             for(int j=2; j<=n; j++)
 40             {
 41                 if( s&(1<<j-1) )    continue;   //只能走1次
 42                 int add= j<=egy?cap:-1;
 43                 if( dis[i][j]>0 && dp[s][i]>=dis[i][j] )   //前提要走得到那个位置
 44                     dp[s|(1<<j-1)][j]=max(dp[s|(1<<j-1)][j], max(dp[s][i]-dis[i][j],add));
 45             }
 46         }
 47     }
 48     int mod=(1<<n)-(1<<egy)+1, ans=-INF;
 49     for(int s=1; s<(1<<n); s++)
 50     {
 51         if( (s&mod)==mod )
 52         {
 53             for(int i=2; i<=n; i++)
 54                 ans=max(ans, dp[s][i]);
 55         }
 56     }
 57     return ans>=0;
 58 }
 59 
 60 deque<node>  que;
 61 void BFS(int x,int y)   //求最短路
 62 {
 63     memset(vis,0,sizeof(vis));
 64     que.clear();
 65     que.push_back(node(x,y,0));
 66     vis[x][y]=true;
 67     while(!que.empty())
 68     {
 69         node t=que.front();que.pop_front();
 70         if( istar(t.x,t.y) )    //记录最短路
 71             dis[tag[x][y]][tag[t.x][t.y]]=t.d;
 72         if(isok(t.x-1,t.y)&&!vis[t.x-1][t.y])
 73         {
 74             que.push_back(node(t.x-1,t.y,t.d+1) );
 75             vis[t.x-1][t.y]=true;
 76         }
 77         if(isok(t.x+1,t.y)&&!vis[t.x+1][t.y])
 78         {
 79             que.push_back(node(t.x+1,t.y,t.d+1) );
 80             vis[t.x+1][t.y]=true;
 81         }
 82         if(isok(t.x,t.y-1)&&!vis[t.x][t.y-1])
 83         {
 84             que.push_back(node(t.x,t.y-1,t.d+1) );
 85             vis[t.x][t.y-1]=true;
 86         }
 87         if(isok(t.x,t.y+1)&&!vis[t.x][t.y+1])
 88         {
 89             que.push_back(node(t.x,t.y+1,t.d+1) );
 90             vis[t.x][t.y+1]=true;
 91         }
 92     }
 93 }
 94 
 95 
 96 int cal()
 97 {
 98     vector<node> vect;   //关键点
 99     for(int i=1; i<=n; i++)    //编号
100         for(int j=1; j<=m; j++)
101             if(g[i][j]!='S'&&g[i][j]!='D')
102                 vect.push_back(node(i,j,0));
103 
104     sort(vect.begin(), vect.end(), cmp);    //按字典序排序
105     for(int i=0; i<vect.size(); i++)      //编号
106         tag[vect[i].x][vect[i].y]=i+1;
107 
108     egy=1;
109     for(int i=0; i<vect.size(); i++)
110     {
111         if(g[vect[i].x][vect[i].y]=='G')    egy++;
112         BFS(vect[i].x,vect[i].y);       //计算最短路
113     }
114     n=vect.size();
115     if(egy==n)    return 0;   //无开关?
116     if(!binary(1000))       return -1;   //不可达
117 
118     int L=0, R=300;
119     while(L<R)      //二分答案
120     {
121         int mid=R-(R-L+1)/2;
122         if( binary(mid) )   R=mid;
123         else                L=mid+1;
124     }
125     return R;
126 }
127 
128 int main()
129 {
130     //freopen("input.txt","r",stdin);
131     while(scanf("%d%d",&n,&m), n+m)
132     {
133         memset(tag,0,sizeof(tag));
134         memset(dis,-1,sizeof(dis));
135 
136         for(int i=1; i<=n; i++)    scanf("%s",g[i]+1);
137         printf("%d
",cal());
138     }
139     return 0;
140 }
AC代码
原文地址:https://www.cnblogs.com/xcw0754/p/4861537.html