[kuangbin带你飞]专题一 简单搜索

来源:互联网 发布:金庸 知乎 编辑:程序博客网 时间:2024/05/17 02:10

感觉写完题目,写下题解还是有些帮助的。

A 棋盘问题 [POJ 1321]

题意:

给定n * n 的棋盘,划定一些可以放置棋子的地方,并要求同一行同一列最多只能有一个棋子。问放置k个棋子的放置方案数。

题解:

简单的DFS,枚举所有合理的方案即可。
DFS时,按行枚举,每一行可以在所有可以放置棋子的地方任选一个放置,或者不放置。在放置过程用个一维数组标记已放置的列,在后续放置中不能在这些列放置。
简单剪枝,去除剩余棋子数大于未放置的行数的情况。

#include <cstdio>#include <cstring>using namespace std;char maze[10][10];bool vis[10];int n, k, ans;void Print(){  for(int i = 1; i <= n; i++)    printf("%d ", vis[i]);  printf("\n");}void dfs(int row, int res){  if(res == 0){  //  printf("%d %d\n", row, res);    //Print();    ans++;    return;  }  if(row > k + 1)  return;  for(int i = 1; i <= n; i++)  {    if(maze[row][i] == '#' && !vis[i])    {      vis[i] = 1;      dfs(row + 1, res - 1);      vis[i] = 0;    }  }  dfs(row + 1, res);}int main(){  while(~scanf("%d%d", &n, &k))  {    if(n == -1 && k == -1)  break;    for(int i = 1; i <= n; i++)      for(int j = 1; j <= n; j++)        scanf(" %c", &maze[i][j]);    ans = 0;    memset(vis, 0, sizeof vis);    dfs(1, k);    printf("%d\n", ans);  }}

B Dungeon Master [POJ - 2251]

题意:

给定一个三维的字符数组,标识每个点的状态,标记了起点和终点的位置,问从起点到终点的最短距离。

题解:

非常经典的迷宫问题,非常经典的BFS,只是从二维变成了三维。
BFS,用队列维护,初始将起点加入队列,维护过程中将当前点可以到达的并且没有走过的位置加入队列,维护到队列为空或者终点入队为止。

#include <cstdio>#include <cstring>#include <queue>using namespace std;int dis[32][32][32];char maze[32][32][32];int x, y, z;int dx[6] = {0, 1, 0, -1, 0, 0}, dy[6] = {1, 0, -1, 0, 0, 0}, dz[6] = {0, 0, 0, 0, 1, -1};struct Node{  int x, y, z;  bool operator == (const Node &t){    return x == t.x && y == t.y && z == t.z;  }}s, e;int main(){  while(~scanf("%d%d%d", &x, &y, &z))  {    if(x == 0 && y == 0 && z == 0)  break;    for(int i = 1; i <= x; i++)      for(int j = 1; j <= y; j++)        for(int k = 1; k <= z; k++)        {          scanf(" %c", &maze[i][j][k]);          if(maze[i][j][k] == 'S'){            s.x = i, s.y = j, s.z = k;          }          else  if(maze[i][j][k] == 'E'){            e.x = i, e.y = j, e.z = k;          }        }    queue<Node> q;    memset(dis, 0x3f3f3f3f, sizeof dis);    q.push(s);    dis[s.x][s.y][s.z] = 0;    while(!q.empty())    {      Node t = q.front(), m; q.pop();      if(t == e)  break;      for(int i = 0; i < 6; i++)      {        m.x = t.x + dx[i], m.y = t.y + dy[i], m.z = t.z + dz[i];        if(1 <= m.x && m.x <= x && 1 <= m.y && m.y <= y && 1 <= m.z && m.z <= z && maze[m.x][m.y][m.z] != '#')        {          if(dis[m.x][m.y][m.z] > dis[t.x][t.y][t.z] + 1)          {            dis[m.x][m.y][m.z] = dis[t.x][t.y][t.z] + 1;            q.push(m);          }        }      }    }    if(dis[e.x][e.y][e.z] < 0x3f3f3f3f) printf("Escaped in %d minute(s).\n", dis[e.x][e.y][e.z]);    else  printf("Trapped!\n");  }}

C - Catch That Cow [POJ - 3278]

题意:

一维方向上,FJ有三种跳跃方式,从点x 到 x - 1,到 x + 1,到2 * x。给定起点和终点,问最短的跳跃步数。

题解:

BFS,用队列维护,初始加入起点,维护过程中将当前点可以到达的三个位置中没有入过队的点都加入队列,维护到队列为空,或终点入队为止。
由于0 <= N <= 100000,当跳到 x < 0的点再跳到终点肯定不是最优的,因此 x < 0 的点都直接抛弃。同理 跳到x >= 200000的点也肯定存在其它更优方案。因此也直接抛弃。

#include <cstdio>#include <queue>#include <cstring>using namespace std;int dis[500010];int main(){  int n, k;  while(~scanf("%d%d", &n, &k))//scanf("%d%d", &n, &k);  {    memset(dis, 0x3f, sizeof dis);    dis[n] = 0;    queue<int> q;    q.push(n);    while(!q.empty())    {      int x = q.front();  q.pop();      if(x == k)  break;      int nex1 = x - 1, nex2 = x + 1, nex3 = x * 2;      if(nex1 >= 0 && dis[nex1] > dis[x] + 1)      {        dis[nex1] = dis[x] + 1;        q.push(nex1);      }      if(dis[nex2] > dis[x] + 1)      {        dis[nex2] = dis[x] + 1;        q.push(nex2);      }      if(nex3 <= 200000 && dis[nex3] > dis[x] + 1)      {        dis[nex3] = dis[x] + 1;        q.push(nex3);      }    }    printf("%d\n", dis[k]);  }}

D - Fliptile [POJ - 3279]

题意:

给出M * N 的矩阵,0表示该点为白, 1为黑,你需要通过踩一些位置来翻转颜色,最终让所有格子都变成白色。当你踩一个点的时,这个点和它相邻的四个点都会翻转颜色。问踩哪些点可以让所有格子都变成白色。

题解:

对于某个格子,踩2次相当于没踩,因此我们只需考虑每个格子踩或不踩。如果直接枚举所有状态是2 ^ (N * M),显然不可行。假设前几行我们枚举完了,对于当前行,我们要使矩阵全为0,显然踩的方法使确定的。因此我们可以枚举第一行的状态,然后接下来所有点是否踩都可以确定,再判一下踩完之后矩阵是否全为零即可。

#include <cstdio>#include <cstring>using namespace std;int m, n;int mat[32][32], tmp[32][32], ans[32][32], ck[32][32], sum, cnt;int dir[5][2] = {0, 0, 1, 0, -1, 0, 0, 1, 0, -1};void flip(int x, int y){  ck[x][y] = 1;  for(int i = 0; i < 5; i++)  {    int mx = x + dir[i][0], my = y + dir[i][1];    if(1 <= mx && mx <= m && 1 <= my && my <= n)      tmp[mx][my] = 1 ^ tmp[mx][my];  }}bool check(){  for(int i = 1; i <= m; i++)    for(int j = 1; j <= n; j++)      if(tmp[i][j]) return 0;  return 1;}void Print(){  printf("\n");  for(int i = 1; i <= m; i++)  {    for(int j = 1; j <= n; j++)      printf("%d ", tmp[i][j]);    printf("\n");  }  printf("\n");}void solve(int sta){  memcpy(tmp, mat, sizeof tmp);  memset(ck, 0, sizeof ck);  cnt = 0;  for(int i = 0; i < m; i++)    if((sta >> i) & 1){      flip(1, i + 1);      cnt++;    }  for(int i = 1; i < m; i++)    for(int j = 1; j <= n; j++)      if(tmp[i][j])      {        flip(i + 1, j);        cnt++;      }  //Print();  if(check() && cnt < sum)  {    sum = cnt;    memcpy(ans, ck, sizeof ck);  }}int main(){    while(~scanf("%d%d", &m, &n))    {      for(int i = 1; i <= m; i++)        for(int j = 1; j <= n; j++)          scanf("%d", &mat[i][j]);      sum = 0x3f3f3f3f;      memset(ans, 0, sizeof ans);      memset(ck, 0, sizeof ck);      int tot = 1 << m;      for(int i = 0; i < tot; i++)        solve(i);      if(sum < 0x3f3f3f3f){        for(int i = 1; i <= m; i++)        {          for(int j = 1; j <= n; j++)            printf("%d ", ans[i][j]);          printf("\n");        }      }      else  printf("IMPOSSIBLE\n");    }}

E - Find The Multiple POJ - 1426

题意:

求一个仅由0和1构成的能整除n的数(10进制)。

题解:

用BFS从小到大枚举所有的数,能整除即输出。n <= 200,直接上long long并没有超。

#include <cstdio>#include <queue>using namespace std;#define ll long longint main(){  int n;  while(~scanf("%d", &n))  {    if(n == 0)  break;    queue<ll> q;    q.push(1);    while(!q.empty())    {      ll x = q.front(); q.pop();      if(x % n == 0){        printf("%lld\n", x);        break;      }      q.push(x * 10);      q.push(x * 10 + 1);    }  }}

F - Prime Path [POJ - 3126]

题意:

给定两个四位素数,每次可以改变一个位的数字,问从一个素数变到另一个素数的最少的次数,要求每次改变后的数都必须是素数。

题解:

直接BFS,枚举当前点所有可到达的点并且没有走过的点。

#include <cstdio>#include <cmath>#include <algorithm>#include <queue>#include <cstring>using namespace std;const int maxn = 10010;const int inf = 0x3f3f3f3f;int stp[maxn], pre[maxn];bool isprime(int x){  for(int i = 2; i <= sqrt(x); i++)    if(x % i == 0)  return 0;  return 1;}int main(){  int T;  scanf("%d", &T);  while(T--)  {    int a, b;    scanf("%d%d", &a, &b);    memset(stp, inf, sizeof stp);    memset(pre, -1, sizeof pre);    queue<int> q;    q.push(a);    stp[a] = 0;    while(!q.empty())    {      int t = q.front();  q.pop();      if(t == b)  break;      for(int i = -9; i <= 9; i++)        for(int j = 1; j <= 4; j++)        {          int dt = 1, dig = t;          for(int k = 0; k < j - 1; k++)            dt *= 10;          for(int k = j; k > 1; k--)            dig /= 10;          dig %= 10;          if(dig + i < 0 || dig + i > 9)  continue;          int x = t + i * dt;        //          if(1000 <= x && x <= 9999 && isprime(x) && stp[x] > stp[t] + 1)          {            stp[x] = stp[t] + 1;            pre[x] = t;          //  printf("%d %d\n", x, stp[x]);            q.push(x);          }        }    }    int t = b;    /*while(t != -1)    {      printf("%d\n", t);      t = pre[t];    }*/    if(stp[b] == inf) printf("Impossible\n");    else printf("%d\n", stp[b]);  }}

G - Shuffle’m Up [POJ - 3087]

题意:

给你两堆牌,每次会把两堆牌合成一堆并且间隔放置,再次洗牌时,会把这堆牌再分为两堆。问洗多少次后,牌会变成给定的状态。

题解:

每洗牌的结果都唯一,只需要模拟,一次一次洗下去即可。当出现了以前出现过的状态或出现所求的状态时即洗牌结束。用一个map标记状态。

#include <cstdio>#include <string>#include <map>#include <iostream>using namespace std;string buf, ed;char s[2][2010], ur[2010], ss[2010];map<string, int> st;int main(){  int T, cas = 1;  scanf("%d", &T);  while(T--)  {    int n;    scanf("%d", &n);    scanf(" %s %s", s[1], s[0]);    scanf(" %s", ur);    ed.assign(ur);    st.clear();    int cnt = 0;    while(1)    {      for(int i = 0; i < 2 * n; i++)      {        ss[i] = s[i % 2][i / 2];      }      ss[2 * n] = 0;      buf.assign(ss);      //cout << ss << endl;      if(st[buf] || st[ed]) break;      else  st[buf] = ++cnt;      //cout << buf << " " << st[buf] << endl;      for(int i = 0; i < n; i++)        s[1][i] = ss[i];      s[1][n] = 0;      for(int i = 0; i < n; i++)        s[0][i] = ss[n + i];      s[0][n] = 0;    }    buf.assign(ur);    int ans = st[buf] ? st[buf]: -1;    printf("%d %d\n", cas++, ans);  }}

H - Pots [POJ - 3414]

题意:

给两个没有刻度的瓶子,容量为A和B,问是否能够倒出C单位的水。允许的操作为倒满一个瓶子,倒空一个瓶子,将一个瓶子中的水倒入另一个瓶子。输出方案。

题解:

用pair 标记两个瓶子现在储存的水,然后枚举6种操作BFS,直到一个瓶子中的水量为C或队列为空为止。算法挺简单的,就是基本的BFS,只是6种操作都需要手动枚举,另外是输出方案。因此需要标记状态转移的过程,用一个pre标记当前点的前置点,用sta标记变成当前点的操作方式。然后从结束节点从后往前加入栈中,再从栈中输出。写起来比较繁琐,代码量有点大。

#include <cstdio>#include <map>#include <algorithm>#include <queue>#include <stack>using namespace std;typedef pair<int, int> pii;#define fir first#define sec secondmap<pii, int> mmp;map<pii, pii> pre;map<pii, int> sta;int main(){  int S;  int Mx, My;  scanf("%d%d%d", &Mx, &My, &S);  mmp.clear();  pre.clear();  queue<pii> q;  pii t(0, 0);  pii ed;  mmp[t] = 1;  q.push(t);  int ans = -1;  while(!q.empty())  {    t = q.front();  q.pop();    pii at;    int x = t.fir, y = t.sec;    //printf("%d %d %d\n", x, y, mmp[t]);    if(x == S || y == S){      ans = mmp[t];      ed = t;      break;    }    if(x < Mx)    {      at.fir = Mx, at.sec = y;      if(mmp[at] == 0 || mmp[at] > mmp[t] + 1)      {        mmp[at] = mmp[t] + 1;        pre[at] = t;        sta[at] = 1;        q.push(at);      }    }    if(y < My)    {      at.fir = x, at.sec = My;      if(mmp[at] == 0 || mmp[at] > mmp[t] + 1)      {        mmp[at] = mmp[t] + 1;        pre[at] = t;        sta[at] = 2;        q.push(at);      }    }    if(x > 0)    {      at.fir = 0, at.sec = y;      if(mmp[at] == 0 || mmp[at] > mmp[t] + 1)      {        mmp[at] = mmp[t] + 1;        pre[at] = t;        sta[at] = 3;        q.push(at);      }      int d = min(x, My - y);      at.fir = x - d, at.sec = y + d;      if(mmp[at] == 0 || mmp[at] > mmp[t] + 1)      {        mmp[at] = mmp[t] + 1;        pre[at] = t;        sta[at] = 4;        q.push(at);      }    }    if(y > 0)    {      at.fir = x, at.sec = 0;      if(mmp[at] == 0 || mmp[at] > mmp[t] + 1)      {        mmp[at] = mmp[t] + 1;        pre[at] = t;        sta[at] = 5;        q.push(at);      }      int d = min(y, Mx - x);      at.fir = x + d, at.sec = y - d;      if(mmp[at] == 0 || mmp[at] > mmp[t] + 1)      {        mmp[at] = mmp[t] + 1;        pre[at] = t;        sta[at] = 6;        q.push(at);      }    }  }  stack<int> s;  t.fir = 0, t.sec = 0;  while(ed != t)  {    s.push(sta[ed]);    ed = pre[ed];  }  if(ans == -1) printf("impossible\n");  else{    printf("%d\n", ans - 1);    while(!s.empty())    {      int t = s.top();  s.pop();      if(t == 1)  printf("FILL(1)\n");      else if(t == 2) printf("FILL(2)\n");      else if(t == 3) printf("DROP(1)\n");      else if(t == 4) printf("POUR(1,2)\n");      else if(t == 5) printf("DROP(2)\n");      else  printf("POUR(2,1)\n");    }  }}

I - Fire Game [FZU - 2150]

题意:

给定一个N * M 的矩阵,一些格子中有草“#”,另一些格子中则为空地。从其中两个有草的点点火,问最短多长时间能烧光所有草地。

题解:

数据范围较小,直接枚举两个点,BFS求一遍烧光所有草地的时间。取所有情况中的最小值即可。

#include <cstdio>#include <cstring>#include <queue>#include <algorithm>using namespace std;typedef pair<int, int> pii;#define mp make_pair#define fir first#define sec secondchar maze[32][32];int n, m;int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};int dis[32][32];int ans = 0;int bfs(int x1, int y1, int x2, int y2){  memset(dis, 0x3f, sizeof dis);  dis[x1][y1] = 0;  dis[x2][y2] = 0;  queue<pii > q;  q.push(mp(x1, y1));  q.push(mp(x2, y2));  while(!q.empty())  {    pii t = q.front();  q.pop();    int x = t.fir, y = t.sec;    for(int k = 0; k < 4; k++)    {      int mx = x + dx[k], my = y + dy[k];      if(1 <= mx && mx <= n && 1 <= my && my <= m && maze[mx][my] == '#' && dis[mx][my] > dis[x][y] + 1)      {        dis[mx][my] = dis[x][y] + 1;        q.push(mp(mx, my));      }    }  }  int ret = 0;  for(int i = 1; i <= n; i++)    for(int j = 1; j <= m; j++)      if(maze[i][j] == '#')        ret = max(ret, dis[i][j]);//  printf("%d %d %d %d %d\n",x1, y1, x2, y2, ret);  ans = min(ans, ret);}int main(){  int T, cas = 1;  scanf("%d", &T);  while(T--)  {    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++)      for(int j = 1; j <= m; j++)        scanf(" %c", &maze[i][j]);      ans = 0x3f3f3f3f;      for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++)        {          if(maze[i][j] == '#')          {            for(int p = 1; p <= n; p++)              for(int q = 1; q <= m; q++)                if(maze[p][q] == '#')                {                  bfs(i, j, p, q);                }          }        }    if(ans == 0x3f3f3f3f) ans = -1;    printf("Case %d: %d\n", cas++, ans);  }}

J - Fire! UVA - 11624

题意:

一个二维字符矩阵,J代表人的初始位置,F代表火的位置,每个单位时间人可以移动一步,火向四周蔓延。求逃离的最短时间。

题解:

第一遍BFS跑出火蔓延到所有点的时间。第二遍BFS跑出人到达某一个点的最短时间,每一个的下个可移动的位置必须满足人到达这点的时间早于火蔓延到该点的时间。
注意火的初始点可能不止一个,因此需要如上分开BFS。

//#define LOCAL#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <vector>#include <map>#include <set>#include <stack>#include <queue>#include <iostream>#include <cstdlib>#include <algorithm>using namespace std;#define LL long long#define ll long long#define ull unsigned long long#define inf INF#define INF 0x3f3f3f3f#define maxn MAX_N#define MOD mod#define MMT(x,i) memset(x,i,sizeof(x))#define rep(i, n) for(int i = 0; i < n; i++)#define FOR(i, n) for(int i = 1; i <= n; i++)#define pb push_back#define mp make_pair#define X first#define Y second#define pii pair<int, int>const LL mod = 1e9 + 7;const double pi = acos(-1.0);const double e = exp(1);const double eps = 1e-8;const int maxn = 1010;char maze[maxn][maxn];int dis[maxn][maxn];int fire[maxn][maxn];int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};int main(){  int T;  scanf("%d", &T);  while(T--)  {    int n, m;    scanf("%d%d", &n, &m);    queue<pii> pj, pf;    memset(dis, 0x3f, sizeof dis);    memset(fire, 0x3f, sizeof fire);    FOR(i, n)      FOR(j, m)      {        scanf(" %c", &maze[i][j]);        if(maze[i][j] == 'F'){          pf.push(mp(i, j));          fire[i][j] = 0;        }        else if(maze[i][j] == 'J'){          pj.push(mp(i, j));          dis[i][j] = 0;        }      }    FOR(i, n)      maze[i][0] = maze[i][m + 1] = 'E';    FOR(i, m)      maze[0][i] = maze[n + 1][i] = 'E';    int ans = 0x3f3f3f3f;    while(!pf.empty())    {      pii F = pf.front(); pf.pop();      int xf = F.X, yf = F.Y;      for(int i = 0; i < 4; i++)      {        int mxf = xf + dx[i], myf = yf + dy[i];        if(1 <= mxf && mxf <= n && 1 <= myf && myf <= m && maze[mxf][myf] != '#' && fire[mxf][myf] > fire[xf][yf] + 1)        {          fire[mxf][myf] = fire[xf][yf] + 1;          pf.push(mp(mxf, myf));        }      }    }    while(!pj.empty())    {      pii J = pj.front(); pj.pop();      int xj = J.X, yj = J.Y;      for(int i = 0; i < 4; i++)      {        int mxj = xj + dx[i], myj = yj + dy[i];        if(0 <= mxj && mxj <= n + 1 && 0 <= myj && myj <= m + 1 && maze[mxj][myj] != '#' && fire[mxj][myj] > dis[xj][yj] + 1 && dis[mxj][myj] > dis[xj][yj] + 1)        {          dis[mxj][myj] = dis[xj][yj] + 1;      //    printf("%d %d %d %d %d\n", mxj, myj, dis[mxj][myj], xj, yj);          pj.push(mp(mxj, myj));        }      }    }    FOR(i, n)      ans = min(ans, min(dis[i][0], dis[i][m + 1]));    FOR(i, m)      ans = min(ans, min(dis[0][i], dis[n + 1][i]));    if(ans == 0x3f3f3f3f) printf("IMPOSSIBLE\n");    else      printf("%d\n", ans);  }}

K - 迷宫问题 [POJ - 3984]

题意:

给出一个5 * 5 的迷宫,求逃离的最短路径,输出路径。

题解:

非常经典的BFS迷宫问题,不过我严重怀疑POJ只有一组测试数据。输出路径,用一个pre记录每一次转移时当前点的前置点。最后从终点回溯,加到栈里,再输出。

#include <cstdio>#include <cstring>#include <queue>#include <map>#include <stack>using namespace std;#define pii pair<int, int>#define mp make_pair#define fir first#define sec secondint maze[10][10];int dis[10][10];map<pii, pii> pre;int n = 5, m = 5;int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};int main(){  for(int i = 0; i < n; i++)    for(int j = 0; j < m; j++)      scanf("%d", &maze[i][j]);  memset(dis, 0x3f, sizeof dis);  pii st(0, 0), ed(4, 4);  dis[0][0] = 0;  queue<pii > q;  q.push(st);  while(!q.empty())  {    pii t = q.front();  q.pop();    int x = t.fir, y = t.sec;    if(t == ed) break;    for(int i = 0; i < 4; i++)    {      int mx = x + dx[i], my = y + dy[i];      if(0 <= mx && mx < n && 0 <= my && my < m && !maze[mx][my] && dis[mx][my] > dis[x][y] + 1)      {        dis[mx][my] = dis[x][y] + 1;        pre[mp(mx, my)] = mp(x, y);      //  printf("%d %d\n", mx, my);        q.push(mp(mx, my));      }    }  }  stack<pii > s;  while(ed != st)  {    s.push(ed);    ed = pre[ed];  }  s.push(st);  while(!s.empty())  {    pii t = s.top();  s.pop();    printf("(%d, %d)\n", t.fir, t.sec);  }}

L - Oil Deposits [HDU - 1241]

题意:

给出n * m 个点, ‘@’为油井。问矩阵内油库的个数。相邻的油井构成油库。

题解:

求八联通块的个数。可以用BFS求。我习惯用并查集,就用并查集维护了。

//#define LOCAL#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <vector>#include <map>#include <set>#include <stack>#include <queue>#include <iostream>#include <cstdlib>#include <algorithm>using namespace std;#define LL long long#define ll long long#define ull unsigned long long#define inf INF#define rep REP#define INF 0x3f3f3f3f#define maxn MAX_N#define MOD mod#define MMT(x,i) memset(x,i,sizeof(x))#define REP(i, n) for(int i = 0; i < n; i++)#define FOR(i, n) for(int i = 1; i <= n; i++)#define pb push_back#define mp make_pair#define X first#define Y secondconst LL MOD = 1e9 + 7;const double pi = acos(-1.0);const double E = exp(1);const double EPS = 1e-8;const int MAX_N = 10010;int par[maxn], ran[maxn];void init(){  for(int i = 1; i < maxn; i++)  {    par[i] = i;    ran[i] = 0;  }}int find(int x){  if(x == par[x]) return x;  else  return par[x] = find(par[x]);}void unite(int x, int y){  x = find(x), y = find(y);  if(ran[x] < ran[y]) par[x] = y;  else  {    par[y] = x;    if(ran[x] == ran[y])  ran[x]++;  }}int n, m;char maze[110][110];int dx[8] = {0, 1, 0, -1, -1, -1, 1, 1}, dy[8] = {1, 0, -1, 0, 1, -1, 1, -1};int root[maxn], cnt[maxn];int main(){  while(~scanf("%d%d", &n, &m))  {    if(n == 0 && m == 0)  break;    init();    FOR(i, n)      FOR(j, m)        scanf(" %c", &maze[i][j]);    memset(cnt, 0, sizeof cnt);    FOR(i, n)      FOR(j, m)      {        if(maze[i][j] == '@')          rep(k, 8)          {            int mx = i + dx[k], my = j + dy[k];            if(1 <= mx && mx <= n && 1 <= my && my <= m && maze[mx][my] == '@')              unite(i * m + j, mx * m + my);          }      }    int ans = 0;    FOR(i, n)      FOR(j, m)        if(maze[i][j] == '@')          cnt[find(i * m + j)]++;    FOR(i, (n + 1) * m)      if(cnt[i])  ans++;    printf("%d\n", ans);  }}

M - 非常可乐 [HDU - 1495]

题意:

中文题目,不作解释。

题解:

用一个节点保存3个瓶子的饮料量,然后直接BFS。

//#define LOCAL#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <vector>#include <map>#include <set>#include <stack>#include <queue>#include <iostream>#include <cstdlib>#include <algorithm>using namespace std;#define LL long long#define ll long long#define ull unsigned long long#define inf INF#define INF 0x3f3f3f3f#define maxn MAX_N#define MOD mod#define MMT(x,i) memset(x,i,sizeof(x))#define REP(i, n) for(int i = 0; i < n; i++)#define FOR(i, n) for(int i = 1; i <= n; i++)#define pb push_back#define mp make_pair#define X first#define Y secondconst LL MOD = 1e9 + 7;const double pi = acos(-1.0);const double E = exp(1);const double EPS = 1e-8;const int MAX_N = 1000010;struct Node{  int a, b, c;  Node(){}  Node(int _a, int _b, int _c){    a = _a, b = _b, c = _c;  }  bool operator < (const Node &t)const{    return a == t.a ? (b == t.b ? c < t.c : b < t.b) : a < t.a;  }};map<Node, int> mmp;int ma, mb, mc;int main(){  while(~scanf("%d%d%d", &mc, &ma, &mb))  {    if(ma == 0 && mb == 0 && mc == 0) break;    queue<Node> q;    Node p(0, 0, mc);    q.push(p);    mmp.clear();    mmp[p] = 0;    int ans = -1, d, na, nb, nc;    Node nex;    if(mc % 2 == 0)      while(!q.empty())      {        Node t = q.front();        q.pop();        if((t.a == mc / 2 || t.b == mc / 2) && t.c == mc / 2)        {          ans = mmp[t];          break;        }        if(t.c)        {          d = min(t.c, ma - t.a);          na = t.a + d, nb = t.b, nc = t.c - d;          nex.a = na, nex.b = nb, nex.c = nc;          if(mmp[nex] == 0 || mmp[nex] > mmp[t] + 1)          {            mmp[nex] = mmp[t] + 1;            q.push(nex);          }          d = min(t.c, mb - t.b);          na = t.a, nb = t.b + d, nc = t.c - d;          nex.a = na, nex.b = nb, nex.c = nc;          if(mmp[nex] == 0 || mmp[nex] > mmp[t] + 1)          {            mmp[nex] = mmp[t] + 1;            q.push(nex);          }        }        if(t.a)        {          d = min(t.a, mb - t.b);          na = t.a - d, nb = t.b + d, nc = t.c;          nex.a = na, nex.b = nb, nex.c = nc;          if(mmp[nex] == 0 || mmp[nex] > mmp[t] + 1)          {            mmp[nex] = mmp[t] + 1;            q.push(nex);          }          d = min(t.a, mc - t.c);          na = t.a - d, nb = t.b, nc = t.c + d;          nex.a = na, nex.b = nb, nex.c = nc;          if(mmp[nex] == 0 || mmp[nex] > mmp[t] + 1)          {            mmp[nex] = mmp[t] + 1;            q.push(nex);          }        }        if(t.b)        {          d = min(t.b, ma - t.a);          na = t.a + d, nb = t.b - d, nc = t.c;          nex.a = na, nex.b = nb, nex.c = nc;          if(mmp[nex] == 0 || mmp[nex] > mmp[t] + 1)          {            mmp[nex] = mmp[t] + 1;            q.push(nex);          }          d = min(t.b, mc - t.c);          na = t.a, nb = t.b - d, nc = t.c + d;          nex.a = na, nex.b = nb, nex.c = nc;          if(mmp[nex] == 0 || mmp[nex] > mmp[t] + 1)          {            mmp[nex] = mmp[t] + 1;            q.push(nex);          }        }      }    if(ans == -1) printf("NO\n");    else  printf("%d\n", ans);  }}

N - Find a way [HDU - 2612]

题意:

给出n * m 的矩阵,标记了KFC的位置,yifenfei 和 Merceki的位置。求两人到达某个KFC的总耗时最短。

题解:

跑两遍BFS即可。

//#define LOCAL#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <vector>#include <map>#include <set>#include <stack>#include <queue>#include <iostream>#include <cstdlib>#include <algorithm>using namespace std;#define LL long long#define ll long long#define ull unsigned long long#define inf INF#define INF 0x3f3f3f3f#define maxn MAX_N#define MOD mod#define MMT(x,i) memset(x,i,sizeof(x))#define REP(i, n) for(int i = 0; i < n; i++)#define FOR(i, n) for(int i = 1; i <= n; i++)#define pb push_back#define mp make_pair#define pii pair<int, int>#define X first#define Y secondconst LL MOD = 1e9 + 7;const double pi = acos(-1.0);const double E = exp(1);const double EPS = 1e-8;const int MAX_N = 210;char maze[maxn][maxn];int Mx, My, Yx, Yy;int dis1[maxn][maxn], dis2[maxn][maxn];int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};int main(){  int n, m;  while(~scanf("%d%d", &n, &m))  {    FOR(i, n)      FOR(j, m)      {        scanf(" %c", &maze[i][j]);        if(maze[i][j] == 'Y') Yx = i, Yy = j;        if(maze[i][j] == 'M') Mx = i, My = j;      }    memset(dis1, 0x3f, sizeof dis1);    memset(dis2, 0x3f, sizeof dis2);    queue<pii > q1, q2;    q1.push(mp(Mx, My));    dis1[Mx][My] = 0;    q2.push(mp(Yx, Yy));    dis2[Yx][Yy] = 0;    while(!q1.empty())    {      pii t = q1.front(); q1.pop();      REP(i, 4)      {        int mx = t.X + dx[i], my = t.Y + dy[i];        if(1 <= mx && mx <= n && 1 <= my && my <= m && maze[mx][my] != '#' && dis1[mx][my] > dis1[t.X][t.Y] + 1)        {          dis1[mx][my] = dis1[t.X][t.Y] + 1;          q1.push(mp(mx, my));        }      }    }    while(!q2.empty())    {      pii t = q2.front(); q2.pop();      REP(i, 4)      {        int mx = t.X + dx[i], my = t.Y + dy[i];        if(1 <= mx && mx <= n && 1 <= my && my <= m && maze[mx][my] != '#' && dis2[mx][my] > dis2[t.X][t.Y] + 1)        {          dis2[mx][my] = dis2[t.X][t.Y] + 1;          q2.push(mp(mx, my));        }      }    }    int ans = 0x3f3f3f3f;    FOR(i, n)      FOR(j, m)        if(maze[i][j] == '@')          ans = min(ans, dis1[i][j] + dis2[i][j]);    printf("%d\n", ans * 11);  }}