zoukankan      html  css  js  c++  java
  • 传教士和野人问题

    题目链接:codeforces上的一次比赛,tourist参加了

    题目描述:

    m个传教士和m个野人撑船过河,船上最多有n个人,最少有一个人(否则没人撑船了).

    野人爱吃人,当他们人多势众时,就会把传教士给吃掉.也就是说,当野人的人数大于传教士的人数时,野人就把传教士全吃掉.

    有三个吃人的地点:船上,左岸,右岸.

    面对这滔滔河水,他们想尽快过河.给定m和n两个正整数,问能不能成功过河?如果能过河,最少需要撑几次船才能全部过去?

    分析

    这是一个经典的益智游戏.

    关键在于状态描述,用三元组来描述(左岸羊的个数,左岸狼的个数,船在左岸还是右岸),符号表示(goat,wolf,boat).

    那么右岸的状态就知道了(m-goat,m-wolf,!boat).

    如何判断一个状态是否合法?

    对于状态(goat,wolf,boat),满足左岸上goat>=wolf,右岸上m-goat>=m-wolf.

    若要这两个不等式同时成立,必有goat=wolf.

    或者是goat=0,羊的个数确实比狼少,狼想吃但没有.

    或者是goat=m,这样跟上面那种情况对称,也是一种安全状态.

    有三种合法状态:

    (0)goat=0

    (1)goat=wolf

    (2)goat=m

    于是,就有了改良了的状态描述方案:用三元组来表示(状态的类别编号,狼的个数,船的位置).空间复杂度骤减为O(m*3*2).

    状态虽然有m*m*2个,但是合法的却只有6*m个.

    状态转移:(g,w,b)的dg+dw<=n.于是有2种运输方案:

    (0)把船所在的那个岸的羊全部运到对岸去,狼随便运,只要不超重.这样产生0,2两类状态.

    (1)把dg个羊和dw个狼运到对岸去,还是保持平衡.这样保持1状态.

    我的方法有点暴力,所以超时了.

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 #include<queue>
     5 #include<math.h>
     6 #include<string.h>
     7 #include<stdlib.h>
     8 using namespace std;
     9 typedef long long ll;
    10 typedef unsigned long long ull;
    11 #define re(i,n) for(int i=0;i<n;i++)    
    12 const int maxn = 1e5 + 7;
    13 int n, m;
    14 struct Node{
    15     int wolf, kind, boat;
    16     int fa;//记录是从那个节点到来的,用于回溯怎么到达这个节点
    17 };
    18 bool vis[maxn][3][2];//有没有访问过这个状态
    19 int f[maxn][3][2];//到达此状态所需的步数
    20 struct Q{
    21     Node a[maxn * 6];
    22     int h, r;
    23     void init(){
    24         memset(vis, 0, sizeof(vis));
    25         h = r = 0;
    26     }
    27     void enq(int wolf, int kind, int boat, int step){
    28         if (vis[wolf][kind][boat])return;
    29         vis[wolf][kind][boat] = true;
    30         f[wolf][kind][boat] = step;
    31         a[r].wolf = wolf, a[r].boat = boat, a[r].kind = kind;
    32         a[r].fa = h - 1;
    33         r++;
    34     }
    35     Node deq(){
    36         return a[h++];
    37     }
    38 }q;
    39 int go(){
    40     q.init();
    41     q.enq(m, 2, 0, 0);
    42     while (q.h != q.r){
    43         Node now = q.deq();
    44         if (now.kind == 2 && now.boat == 1 && now.wolf == m)
    45             return f[now.wolf][now.kind][now.boat];
    46         int w = now.wolf, g;
    47         int step = f[now.wolf][now.kind][now.boat] + 1;
    48         if (now.kind == 0)g = 0;
    49         else if (now.kind == 1)g = w;
    50         else if (now.kind == 2)g = m;
    51         /*只有两种决策方案:把羊全部运过去
    52         运相同数量的狼和羊
    53         */
    54         //羊的个数不为0且小于n,把羊全部运过去.生成2状态.
    55         if (g <= n){
    56             int sp = n - g;
    57             //把狼运过去,至少运一只
    58             for (int i = 1; i <= w&&i <= sp; i++){
    59                 q.enq(m - w + i, 2, 1 - now.boat, step);
    60             }
    61             //狼1只也不运,只运羊就行了
    62             if (g)q.enq(m - w, 2, 1 - now.boat, step);
    63         }
    64         //如果全是羊,但一只羊也不运过去,那就至少运去一只狼.生成0状态
    65         if (g == m){
    66             for (int i = 1; i <= w&&i <= n; i++){
    67                 q.enq(m - w + i, 0, 1 - now.boat, step);
    68             }
    69         }
    70         //运相等数量的狼羊,并且羊不能全部运过去,且至少运过去一只,只有1状态可以生成1状态
    71         if (now.kind == 1){
    72             int by = min(g - 1, w);
    73             by = min(by, n >> 1);
    74             for (int i = 1; i <= by; i++){
    75                 q.enq(m - w + i, 1, 1 - now.boat, step);
    76             }
    77         }
    78         //我这边羊满着,运一部分过去,使得两岸相等.由2状态可以到1状态,
    79         if (g == m&&w < m&&n >= m - w){//w<g必为2状态
    80             //需要运过去m-w只羊,因为河对岸有m-w只狼在等着
    81             q.enq(m - w, 1, 1 - now.boat, step);
    82         }
    83     }
    84     return -1;
    85 }
    86 int main(){
    87     freopen("in.txt", "r", stdin);
    88     for (m = 1; m < 100; m++){
    89         for (n = 1; n <= m*2; n++){
    90             int ans = go();
    91             printf("%3d", ans);
    92         }
    93         puts("");
    94     }
    95     return 0;
    96 }

    我还不会,先贴上那次比赛中的几份神代码.第一份是正确的,后两份是错误的.

      1 import java.io.PrintWriter;
      2 import java.util.Arrays;
      3 import java.util.BitSet;
      4 import java.util.HashSet;
      5 import java.util.Scanner;
      6 
      7 public class F5 {
      8     Scanner in;
      9     PrintWriter out;
     10 //    String INPUT = "100000 10";
     11     String INPUT = "";
     12     
     13     int[] dec(int n, int m)
     14     {
     15         if(n <= m){
     16             return new int[]{n, 0};
     17         }else if(n <= 2 * m){
     18             return new int[]{n - m, n - m};
     19         }else{
     20             return new int[]{n - 2 * m - 1, m};
     21         }
     22     }
     23     
     24     int enc(int x, int y, int m)
     25     {
     26         if(y == 0){
     27             return x;
     28         }else if(x == y){
     29             return x + m;
     30         }else{
     31             return x + 2 * m + 1;
     32         }
     33     }
     34     
     35     void solve()
     36     {
     37         int m = ni();
     38         int n = ni();
     39         if(n <= Math.sqrt(Math.sqrt(m))){
     40             solveH(m, n);
     41         }else{
     42             solveB(m, n);
     43         }
     44     }
     45     
     46     void solveB(int m, int n){
     47             // (a, 0), (a, a), (a, m)
     48             BitSet visitedo = new BitSet();
     49             BitSet visitedh = new BitSet();
     50             BitSet q = new BitSet();
     51             q.set(0);
     52             boolean iso = true;
     53             int step = 0;
     54             
     55             outer:
     56             while(!q.isEmpty()){
     57 //                tr(step, q);
     58                 BitSet nq = new BitSet();
     59                 if(iso){
     60                     visitedh.or(q);
     61                 }else{
     62                     visitedo.or(q);
     63                 }
     64                 for(int cur = q.nextSetBit(0);cur != -1;cur = q.nextSetBit(cur+1)){
     65                     if(!iso && cur == 2 * m)break outer;
     66                     int[] co = dec(cur, m);
     67                     if(iso){
     68                         {
     69                             if(co[1] == 0){
     70                                 int inf = co[0] + 1;
     71                                 int sup = Math.min(m, co[0] + n);
     72                                 if(inf <= sup){
     73                                     nq.set(enc(inf, 0, m), enc(sup, 0, m) + 1);
     74                                 }
     75                             }
     76                         }
     77                         {
     78                             int inf = Math.max(co[0], co[1]);
     79                             if(co[0] == co[1])inf++;
     80                             int sup = Math.min(m, (n + co[0] + co[1]) / 2);
     81                             
     82                             if(inf <= sup){
     83                                 nq.set(enc(inf, inf, m), enc(sup, sup, m) + 1);
     84                             }
     85                         }
     86                         {
     87                             int inf = co[0];
     88                             if(co[1] == m)inf++;
     89                             int sup = Math.min(m - 1, co[0] + n - m + co[1]);
     90                             if(inf <= sup){
     91                                 nq.set(enc(inf, m, m), enc(sup, m, m) + 1);
     92                             }
     93                         }
     94                     }else{
     95                         {
     96                             if(co[1] == m){
     97                                 int inf = co[0] - 1;
     98                                 int sup = Math.max(0, co[0] - n);
     99                                 if(sup <= inf){
    100                                     nq.set(enc(sup, m, m), enc(inf, m, m) + 1);
    101                                 }
    102                             }
    103                         }
    104                         {
    105                             int inf = Math.min(co[0], co[1]);
    106                             if(co[0] == co[1])inf--;
    107                             int sup = Math.max(0, (-n + co[0] + co[1]) / 2);
    108                             
    109                             if(sup == 0)sup++;
    110                             if(sup <= inf){
    111                                 nq.set(enc(sup, sup, m), enc(inf, inf, m) + 1);
    112                             }
    113                         }
    114                         {
    115                             int inf = co[0];
    116                             if(co[1] == 0)inf--;
    117                             int sup = Math.max(0, co[0] - n + co[1]);
    118                             if(sup <= inf){
    119                                 nq.set(enc(sup, 0, m), enc(inf, 0, m) + 1);
    120                             }
    121                         }
    122                     }
    123                 }
    124                 if(iso){
    125                     nq.andNot(visitedo);
    126                 }else{
    127                     nq.andNot(visitedh);
    128                 }
    129                 q = nq;
    130                 step++;
    131                 iso = !iso;
    132             }
    133             if(q.isEmpty()){
    134                 out.println(-1);
    135             }else{
    136                 out.println(step);
    137             }
    138     }
    139     
    140     void solveH(int m, int n){
    141         
    142         // (a, 0), (a, a), (a, m)
    143         HashSet<Integer> visitedo = new HashSet<Integer>();
    144         HashSet<Integer> visitedh = new HashSet<Integer>();
    145         HashSet<Integer> q = new HashSet<Integer>();
    146         q.add(0);
    147         boolean iso = true;
    148         int step = 0;
    149         
    150         outer:
    151         while(!q.isEmpty()){
    152 //            tr(step, q);
    153             HashSet<Integer> nq = new HashSet<Integer>();
    154             if(iso){
    155                 visitedh.addAll(q);
    156             }else{
    157                 visitedo.addAll(q);
    158             }
    159             for(int cur : q){
    160                 if(!iso && cur == 2 * m)break outer;
    161                 int[] co = dec(cur, m);
    162                 if(iso){
    163                     {
    164                         if(co[1] == 0){
    165                             int inf = co[0] + 1;
    166                             int sup = Math.min(m, co[0] + n);
    167                             if(inf <= sup){
    168                                 for(int i = enc(inf, 0, m);i <= enc(sup, 0, m);i++){
    169                                     nq.add(i);
    170                                 }
    171                             }
    172                         }
    173                     }
    174                     {
    175                         int inf = Math.max(co[0], co[1]);
    176                         if(co[0] == co[1])inf++;
    177                         int sup = Math.min(m, (n + co[0] + co[1]) / 2);
    178                         
    179                         if(inf <= sup){
    180                             for(int i = enc(inf, inf, m);i <= enc(sup, sup, m);i++){
    181                                 nq.add(i);
    182                             }
    183                         }
    184                     }
    185                     {
    186                         int inf = co[0];
    187                         if(co[1] == m)inf++;
    188                         int sup = Math.min(m - 1, co[0] + n - m + co[1]);
    189                         if(inf <= sup){
    190                             for(int i = enc(inf, m, m);i <= enc(sup, m, m);i++){
    191                                 nq.add(i);
    192                             }
    193                         }
    194                     }
    195                 }else{
    196                     {
    197                         if(co[1] == m){
    198                             int inf = co[0] - 1;
    199                             int sup = Math.max(0, co[0] - n);
    200                             if(sup <= inf){
    201                                 for(int i = enc(sup, m, m);i <= enc(inf, m, m);i++){
    202                                     nq.add(i);
    203                                 }
    204                             }
    205                         }
    206                     }
    207                     {
    208                         int inf = Math.min(co[0], co[1]);
    209                         if(co[0] == co[1])inf--;
    210                         int sup = Math.max(0, (-n + co[0] + co[1]) / 2);
    211                         
    212                         if(sup == 0)sup++;
    213                         if(sup <= inf){
    214                             for(int i = enc(sup, sup, m);i <= enc(inf, inf, m);i++){
    215                                 nq.add(i);
    216                             }
    217                         }
    218                     }
    219                     {
    220                         int inf = co[0];
    221                         if(co[1] == 0)inf--;
    222                         int sup = Math.max(0, co[0] - n + co[1]);
    223                         if(sup <= inf){
    224                             for(int i = enc(sup, 0, m);i <= enc(inf, 0, m);i++){
    225                                 nq.add(i);
    226                             }
    227                         }
    228                     }
    229                 }
    230             }
    231             if(iso){
    232                 nq.removeAll(visitedo);
    233             }else{
    234                 nq.removeAll(visitedh);
    235             }
    236             q = nq;
    237             step++;
    238             iso = !iso;
    239         }
    240         if(q.isEmpty()){
    241             out.println(-1);
    242         }else{
    243             out.println(step);
    244         }
    245     }
    246     
    247     void run() throws Exception
    248     {
    249         in = INPUT.isEmpty() ? new Scanner(System.in) : new Scanner(INPUT);
    250         out = new PrintWriter(System.out);
    251 
    252 //        long s = System.currentTimeMillis();
    253         solve();
    254         out.flush();
    255 //        long g = System.currentTimeMillis();
    256 //        System.out.println(g - s + "ms");
    257     }
    258     
    259     
    260     public static void main(String[] args) throws Exception
    261     {
    262         new F5().run();
    263     }
    264     
    265     int ni() { return Integer.parseInt(in.next()); }
    266     void tr(Object... o) { if(INPUT.length() != 0)System.out.println(o.length > 1 || o[0].getClass().isArray() ? Arrays.deepToString(o) : o[0]); }
    267 }

    tourist的代码第十个样例,第十四个样例......是错误的,在比赛开始3小时之前答案是错的,所以tourist过了这道题.之后改了答案,tourist的代码就错了,然而系统没有进行重判,导致tourist一发就过.所以下面的代码是错的.于是乎,全场只有一份代码过了这道题:上面的java代码.

     1 var
     2   yy,zz,n,m,i,e: longint;
     3   lg,rg: array [0..2,0..1,0..110] of longint;
     4   kg: array [0..2,0..1] of longint;
     5   was: array [0..100010,0..2,0..1] of boolean;
     6   x,y,z,km: array [0..666666] of longint;
     7 
     8 procedure put(x1,x2,yy,zz:longint);
     9 var
    10   u1,u2,v,j,xx: longint;
    11 begin
    12   if x2 < 0 then exit;
    13   if x1 > n then exit;
    14   zz:=1-zz;
    15   yy:=2-yy;
    16   if x1 < 0 then x1:=0;
    17   if x2 > n then x2:=n;
    18   xx:=x1; x1:=n-x2; x2:=n-xx;
    19   u1:=x1; u2:=x2;
    20   for v:=1 to kg[yy,zz] do
    21   begin
    22     if x1 < lg[yy,zz,v] then x1:=lg[yy,zz,v];
    23     if x2 > rg[yy,zz,v] then x2:=rg[yy,zz,v];
    24     for j:=x1 to x2 do
    25       if not was[j,yy,zz] then
    26       begin
    27         inc(e);
    28         x[e]:=j;
    29         y[e]:=yy;
    30         z[e]:=zz;
    31         km[e]:=km[i]+1;
    32         was[j,yy,zz]:=True;
    33       end;
    34     if x1 <= x2 then
    35       if (x1 > lg[yy,zz,v]) and (x2 < rg[yy,zz,v]) then
    36       begin
    37         inc(kg[yy,zz]);
    38         lg[yy,zz,kg[yy,zz]]:=x2+1;
    39         rg[yy,zz,kg[yy,zz]]:=rg[yy,zz,v];
    40         rg[yy,zz,v]:=x1-1;
    41       end else
    42       if x2 < rg[yy,zz,v] then lg[yy,zz,v]:=x2+1
    43       else rg[yy,zz,v]:=x1-1;
    44     x1:=u1; x2:=u2;
    45   end;
    46 end;
    47 
    48 begin
    49 //  assign(input,'in'); reset(input);
    50 //  assign(output,'out'); rewrite(output);
    51   read(n,m);
    52   for yy:=0 to 2 do
    53     for zz:=0 to 1 do
    54     begin
    55       kg[yy,zz]:=1;
    56       lg[yy,zz,1]:=0;
    57       rg[yy,zz,1]:=n;
    58     end;
    59   fillchar(was,sizeof(was),False);
    60   i:=1; e:=1;
    61   x[1]:=n; y[1]:=2; z[1]:=0;
    62   km[1]:=0;
    63   was[n,2,0]:=True;
    64   while i <= e do
    65   begin
    66     if (x[i] = n) and (y[i] > 0) and (z[i] = 1) then
    67     begin
    68       writeln(km[i]);
    69       halt;
    70     end;
    71     if y[i] = 0 then
    72     begin
    73       put(x[i]-m,x[i]-1,0,z[i]);
    74     end else
    75     if y[i] = 1 then
    76     begin
    77       put(x[i]-(m shr 1),x[i]-1,1,z[i]);
    78       if (x[i] > 0) and (x[i] <= m) then put(x[i]-(m-x[i]),x[i],0,z[i]);
    79     end else
    80     begin
    81       put(x[i]-m,x[i]-1,2,z[i]);
    82       if (n-x[i]) <= m then
    83         if x[i] < n then put(x[i]-((m-(n-x[i])) shr 1),x[i],1,z[i])
    84         else put(x[i]-(m shr 1),x[i]-1,1,z[i]);
    85     end;
    86     inc(i);
    87   end;
    88   writeln(-1);
    89   close(input);
    90   close(output);
    91 end.

     第二名的代码跟tourist有点像,他的代码是错的.

      1 #include <cstdio>
      2 #include <cmath>
      3 #include <cstring>
      4 #include <string>
      5 #include <sstream>
      6 #include <algorithm>
      7 #include <iostream>
      8 #include <iomanip>
      9 #include <map>
     10 #include <set>
     11 #include <list>
     12 #include <queue>
     13 #include <stack>
     14 #include <vector>
     15 #include <cassert>
     16 
     17 using namespace std;
     18 
     19 #define pb push_back
     20 #define mp make_pair
     21 #define REP(i, n) for (int i = 0; i < (int)(n); ++i)
     22 typedef long long LL;
     23 typedef pair<int, int> PII;
     24 
     25 struct S {
     26     int a, b, c;
     27     S(int a, int b, int c) : a(a), b(b), c(c) {}
     28     S() {}
     29 };
     30 
     31 int m, n, cur;
     32 int d[2][3][35];
     33 queue<S> q;
     34 
     35 bool aCunningPlan() {
     36     return m - cur + 1 <= n;
     37 }
     38 
     39 int updVal;
     40 void upd(int a, int b, int c) {
     41     if (d[a][b][c] == -1) {
     42         d[a][b][c] = updVal;
     43         if (a != 1 || b != 0 || c != m) {
     44             q.push(S(a, b, c));
     45         }
     46     }
     47 }
     48 
     49 int main() {
     50     scanf("%d%d", &m, &n);
     51     if (n < 4 && m > 33) {
     52         printf("-1
    ");
     53         return 0;
     54     }
     55     if (m > 33) {
     56         if (n >= 2 * m) {
     57             printf("1
    ");
     58             return 0;
     59         }
     60         int ans = 0;
     61         cur = 0;
     62         while (true) {
     63             int ncur = min(m, cur + n / 2 - 1);
     64             if (ncur == m) {
     65                 ++ans;
     66                 break;
     67             }
     68             if (aCunningPlan()) {
     69                 ans += 3;
     70                 printf("%d
    ", ans);
     71                 return 0;
     72             }
     73             cur = ncur;
     74             ans += 2;
     75         }
     76         printf("%d
    ", ans);
     77         return 0;
     78     }
     79     REP(i, 2) REP(j, 3) REP(k, m + 1) d[i][j][k] = -1;
     80     d[0][0][0] = 0;
     81     q.push(S(0, 0, 0));
     82     while (!q.empty()) {
     83         S s = q.front();
     84         q.pop();
     85         updVal = d[s.a][s.b][s.c] + 1;
     86         if (s.a == 0) {
     87             if (s.b == 0) {
     88                 for (int nc = s.c + 1; nc <= min(s.c + n / 2, m); ++nc) upd(1, 0, nc);
     89                 if (m - s.c <= n) upd(1, 1, s.c);
     90                 if (s.c == 0) for (int nc = s.c + 1; nc <= min(s.c + n, m); ++nc) upd(1, 2, nc);
     91             } else if (s.b == 1) {
     92                 for (int nc = s.c + 1; nc <= min(s.c + n, m - 1); ++nc) upd(1, 1, nc);
     93                 if (s.c + n >= m) upd(1, 0, m);
     94             } else {
     95                 for (int nc = s.c + 1; nc <= min(s.c + n, m); ++nc) upd(1, 2, nc);
     96                 if (m - s.c + m <= n) upd(1, 0, m);
     97                 if (s.c <= n) upd(1, 0, s.c);
     98             }
     99         } else {
    100             if (s.b == 0) {
    101                 for (int nc = s.c - 1; nc >= max(0, s.c - n / 2); --nc) upd(0, 0, nc);
    102                 if (s.c <= n) upd(0, 2, s.c);
    103             } else if (s.b == 1) {
    104                 for (int nc = s.c - 1; nc >= max(0, s.c - n); --nc) upd(0, 1, nc);
    105                 if (m - s.c <= n) upd(0, 0, s.c);
    106             } else {
    107                 for (int nc = s.c - 1; nc >= max(1, s.c - n); --nc) upd(0, 2, nc);
    108             }
    109         }
    110     }
    111     printf("%d
    ", d[1][0][m]);
    112     return 0;
    113 }
  • 相关阅读:
    AES加解密
    redis HyperLogLog使用
    vi常用操作
    redis string使用
    用jdk命令定位java应用问题
    用户态、内核态及零拷贝
    缓存穿透与雪崩
    ReentrantLock、Semaphore、AQS
    redis list使用
    不同数据库取并集、交集、差集
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/4890061.html
Copyright © 2011-2022 走看看