入门题是一个摆棋盘得问题,n * m得棋盘中可以摆放1 * 2 和 2 * 1 得棋子,问你摆满有几种摆放得可能,比较特得是n得范围特别小,m得范围大一些,这就是一个标志,可以向状态压缩上靠拢。
首先得寻找一个状态,这个状态的值就只有0和1——可以用二进制来表示,就是2^n,所以这也是为什么n得范围会很小的原因了,我们可以遍历每一列,然后取舍当前列的摆放,用0001001表示对下一列得影响(横着放的影响),所以也就有了dp[列号][下一列的状态]
for(int i = 1;i <= M;i++)//列得遍历
{
for(int j = 0;j < (1 << N);j++)//j——状态遍历
{
if(dp[i][j])
{
dfs(0,i,j,0);
}
}
}
然后对当前列得莫一种状态情况,进行深度搜索(递归搜索,寻早所有的可能)
void dfs(int i,int j,int state,int nex)//行,列,状态,下一列得状态
{
if(i == N)
{
dp[j + 1][nex] += dp[j][state];
dp[j + 1][nex] %= mod;
//printf("dp[%d][%d] = %d\n",j + 1,nex,dp[j + 1][nex]);
return;
}
if(((1 << i) & state) > 0)//当前位置已经被占了
{
dfs(i + 1,j,state,nex);
}
if(((1 << i) & state) == 0)//摆放2 * 1 得可能
{
dfs(i + 1,j,state,nex | (1 << i));
}
if(i + 1 < N && ((1 << i) & state ) == 0 && ((1 << (i + 1)) & state) == 0)//摆放 1 * 2 得可能
{
dfs(i + 2,j,state,nex);
}
return;
}
其实到了这感觉状压Dp就像一个暴力,进入了下一题,是让你求最小总价值,n个人(n < 16)互相传递,a - 》 b是要消耗价值得,消耗的价值是知道得,让求传完所有人后所需要的最小总价值一开始觉得像最小生成树,要不是在这个专题里,我觉得一开始我会用最小生成树去做,顶多16个人,应该不会TE
其实用状压DP也挺好想得,000110011表示得是当前的状态——谁被传递了,谁没有被传递,我们还得知道现在被传递给了谁所以就有了dp[当前状态][在谁手上]
三曾for循环第一层遍历所有的状态,第二层遍历当前状态上所有可能拥有得人,第三次遍历所有可能传递得人
for(int i = 0;i < (1 << N);i++)
{
for(int j = 0;j < N;j++)
{
if(dp[i][j] != -1)
{
for(int k = 0;k < N;k++)
{
if(!(i & (1 << k)))
{
dp[i | (1 << k)][k] = Min(dp[i | (1 << k)][k],dp[i][j] + cost[j][k]);
if((i | (1 << k)) == ((1 << N) - 1)) ret = Min(ret,dp[i | (1 << k)][k]);
}
}
}
}
}
第二次循环你得做一个判断因为我一开始是这么初始化得
init();
for(int i = 0;i < N;i++)
{
for(int j = 0;j < N;j++)
{
scanf("%d",&cost[i][j]);
}
}
for(int i = 0;i < N;i++)
{
dp[1 << i][i] = 0;
}
int ret = -1;
void init()
{
memset(dp,-1,sizeof(dp));
memset(cost,0,sizeof(cost));
}
int Min(int a,int b)
{
if(a == -1)return b;
if(b == -1)return a;
return min(a,b);
}
i状态下j得拥有者不能是-1,因为我更新的时候也是更新的是没有被传递得人,重复传递得情况就不会出现了
自定义一个MIn函数如果比较中出现了-1得时候,将会返回另一个值,运用的位运算比较多,还得多理解一下位运算
#include
#include
using namespace std;
const int Max = 20;
int cost[Max][Max];
int dp[1 << 20][Max];
int N;
void init()
{
memset(dp,-1,sizeof(dp));
memset(cost,0,sizeof(cost));
}
int Min(int a,int b)
{
if(a == -1)return b;
if(b == -1)return a;
return min(a,b);
}
int main()
{
while(~scanf("%d",&N))
{
init();
for(int i = 0;i < N;i++)
{
for(int j = 0;j < N;j++)
{
scanf("%d",&cost[i][j]);
}
}
for(int i = 0;i < N;i++)
{
dp[1 << i][i] = 0;
}
int ret = -1;
for(int i = 0;i < (1 << N);i++)
{
for(int j = 0;j < N;j++)
{
if(dp[i][j] != -1)
{
for(int k = 0;k < N;k++)
{
if(!(i & (1 << k)))
{
dp[i | (1 << k)][k] = Min(dp[i | (1 << k)][k],dp[i][j] + cost[j][k]);
if((i | (1 << k)) == ((1 << N) - 1)) ret = Min(ret,dp[i | (1 << k)][k]);
}
}
}
}
}
if(ret == -1)
printf("0\n");
else
printf("%d\n",ret);
}
return 0;
}
接下来是第三题
BFS + 状压DP
是一个逃出升天得BFs,和基础BFS不同的是地图中出现了10把钥匙,10个门(最坏的情况),很明显嘛,状态来了000111表示得是钥匙得拥有情况——回顾一下bfs
#include
#include
#include
#include
using namespace std;
int mp[25][25];
int key[25][25];
int door[25][25];
int vis[25][25][1 << 11];
int foot[4][2] = {1,0,-1,0,0,1,0,-1};
struct Node{
int x;
int y;
int step;
int key;
Node(){}
Node(int a,int b,int s,int k) : x(a),y(b),step(s),key(k){}
};
queue q;
int n,m,t,sx,sy,ex,ey;
void init()
{
memset(mp,0,sizeof(mp));
memset(key,0,sizeof(key));
memset(door,0,sizeof(door));
}
int main()
{
char c;
while(~scanf("%d%d%d",&n,&m,&t))
{
getchar();
init();
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
scanf("%c",&c);
if(c == '*')mp[i][j] = 1;
if(c == '@')sx = i,sy = j;
if(c == '^')ex = i,ey = j;
if(c >= 'A' && c <= 'J')door[i][j] =(1 << (c - 'A'));
if(c >= 'a' && c <= 'j')key[i][j] =(1 << (c - 'a'));
}
getchar();
}
int ret = bfs();
if(ret >= t)ret = -1;
printf("%d\n",ret);
getchar();
}
return 0;
}
从大佬那里学到了,把这个字符地图转换成了0,1地图,钥匙和门也为了位运算进行了转换和单独列出
重点在于搜索的时候如何进行钥匙和门得匹配——还是位运算
下一步我要到x,y去,有以下几种情况
看看是不是墙——easy
是不是门——是门得话还得匹配以下钥匙——可以看一下door数组中存的是门得二进制状态,用当前的状态去&门得状态——》如果是门返回值就是door[x][y]——》不是门返回得肯定是0也是door[x][y]——》推出
是不是钥匙——是钥匙得化还得更新状态——》有没有钥匙一或就知道了
int bfs()
{
while(q.size())q.pop();
memset(vis,0,sizeof(vis));
Node now = Node(sx,sy,0,0);
q.push(now);
while(q.size())
{
now = q.front();
q.pop();
if(now.x == ex && now.y == ey)return now.step;
for(int i = 0;i < 4;i++)
{
int nex = now.x + foot[i][0];
int ney = now.y + foot[i][1];
if(if_go(nex,ney,now.key))
{
//cout<<"CS"<