• 搜索-1


    shzr和搜索的故事:

      沉迷于搜索无法自拔。。。

      记得一开始学搜索的时候,不知道是听了什么谣言,以为搜索只是用来骗分的,不是正经算法,于是正直的我决定不学搜索,似乎noip2017前我的搜索水平止于输出全排列。。。NOIP 2017 PJ T3 ---- 一道搜索可A的题目。。。写了很长很长的dp,只得了10分。当时好naive啊,一个是搜索并不只是用来骗分的,另一个是,骗分又怎么样?难道所有题都写正解吗...后来练了一段时间的搜索,当时的状态就是被young_全方位吊打,尤其是搜索,好多搜索题都是她帮我重构的。最近又在练搜索,不求达到多么高的水平,别被吊打就行(好像立了一个不可能实现的flag)。不多说啦,下面是一些简单的搜索。

    搜索---1

    不需要很多思维和剪枝的搜索,高端搜索请见“搜索-2”。

    BFS的技巧:

      如果边权都相等,可以用于求最短路,$O(N)$,比其他的最短路都快。

      0-1bfs如果搜到边权为0的边,就把新的点插入到队首,如果是边权不为0的边,就把新的点插入到队尾,如果终点出现在队首才能说明找到了最优解。这种思路的运用是非常重要的,下面来看道题。

      开关灯:https://www.luogu.org/problemnew/show/P2845

      题意概述:在一个网格图里走,只能走亮的格子,一开始只有起点是亮的,走到新的格子就可以打开这个格子所控制的所有灯,求一共能打开多少灯。

      如果考虑朴素的BFS会发现不行,因为有的格子走完之后又开了新的灯,此时走回去是可以走到新格子的,但是我们已经把这些可能性排除掉了。这样还有一个方法:BFS 1000 次。然而不仅会超时还不能保证对。这时候我们就想到了刚刚学到的技巧。把可以走到但是因为没开灯所以没走的格子记录下来,如果某一次把这个格子的灯打开了,就把这个格子作为新的BFS起点。这样是对的吗?其实还是不对,因为这里的路早就该走,不应该再排很久的队列道路了,所以把这样的节点插入到BFS的队首,其他扩展出来的节点插入到BFS的队尾,进行BFS。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <queue>
     4 # include <cstring>
     5 # define xx (x+dx[i])
     6 # define yy (y+dy[i])
     7 
     8 using namespace std;
     9 const int dx[]={-1,0,0,1};
    10 const int dy[]={0,1,-1,0};
    11 int tim,cnt,ans,h,n,m,x_1,y_1,x_2,y_2,firs[20009];
    12 deque <int> q;
    13 struct nod
    14 {
    15     int nex,too;
    16 }g[500009];
    17 bool ope[100][100],can_vis[100][100],vis[100][100];
    18 
    19 void add (int x,int y)
    20 {
    21     g[++h].too=y;
    22     g[h].nex=firs[x];
    23     firs[x]=h;
    24 }
    25 
    26 void bfs ()
    27 {
    28     int x,y,beg;
    29     int j;
    30     q.push_back(n+2);
    31     while (q.size())
    32     {
    33         beg=q.front();
    34         q.pop_front();
    35         x=beg/(n+1);
    36         y=beg%(n+1);
    37         if(vis[x][y]) continue;
    38         vis[x][y]=true;
    39         for (int i=firs[beg];i;i=g[i].nex)
    40         {
    41             j=g[i].too;
    42             ope[j/(n+1)][j%(n+1)]=true;
    43             if (can_vis[j/(n+1)][j%(n+1)]&&vis[j/(n+1)][j%(n+1)]==false)
    44                 q.push_front((j/(n+1))*(n+1)+j%(n+1));
    45         }
    46         for (int i=0;i<4;++i)
    47         {
    48             if(xx<1||xx>n||yy<1||yy>n) continue;
    49             can_vis[xx][yy]=true;
    50             if(!ope[xx][yy]) continue;
    51             q.push_back(xx*(n+1)+yy);
    52         }
    53     }
    54 }
    55 
    56 int main()
    57 {
    58     scanf("%d%d",&n,&m);
    59     for (int i=1;i<=m;++i)
    60     {
    61         scanf("%d%d%d%d",&x_1,&y_1,&x_2,&y_2);
    62         add(x_1*(n+1)+y_1,x_2*(n+1)+y_2);
    63     }
    64     ope[1][1]=true;
    65     bfs();
    66     for (int i=1;i<=n;++i)
    67         for (int j=1;j<=n;++j)
    68             if(ope[i][j]) ans++;
    69     printf("%d",ans);
    70     return 0;
    71 }
    开关灯

    排列组合类:

      组合的输出:https://www.luogu.org/problemnew/show/P1157

      题意概述:输出n个数的组合。

      与排列不同,不考虑顺序,所以只好人为的去钦定一些顺序,比如每一个都比上一个大。

      
    # include <cstdio>
    # include <iostream>
    # define R register
    
    using namespace std;
    
    int n,r;
    bool vis[25]={false};
    int ans[30]={0};
    
    void write()
    {
        for (R int i=1;i<=r;i++)
          printf("%3d",ans[i]);
        printf("
    ");
    }
    
    void dfs(int x)
    {
        if(x==r+1)
          write();
        else
        {
            for (R int i=1;i<=n;i++)
              if(!vis[i]&&i>ans[x-1])
              {
                  ans[x]=i;
                  vis[i]=true;
                  dfs(x+1);
                  ans[x]=0;
                  vis[i]=false;
              }
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&r);
        dfs(1);
        return 0;
    }
    View Code

      有重复元素的排列问题:https://www.luogu.org/problemnew/show/P1691

      题意概述:输出n个字母的全排列,相同的字母不进行区分。

      思想很巧妙,直接看代码吧。

      
    # include <cstdio>
    # include <iostream>
    
    using namespace std;
    
    int n,S;
    int vis[30];
    char c,ans[501];
    
    void write()
    {
        for (int i=1;i<=n;i++)
            printf("%c",ans[i]);
        printf("
    ");
        S++;
    }
    
    void dfs(int x)
    {
        if(x==n+1)
        {
            write();
            return ;
        }
        for (int i=0;i<26;i++)
            if(vis[i])
            {
                ans[x]=i+'a';
                vis[i]--;
                dfs(x+1);
                vis[i]++;
            }
    }
    
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            c=getchar();
            while (c<'a'||c>'z') c=getchar();
            vis[c-'a']++;
        }
        dfs(1);
        printf("%d",S);
        return 0;
    }
    View Code

      全排列问题:https://www.luogu.org/problemnew/show/P1706

      题意概述:输出1-n的全排列。

      。。。只是为了凑个整。

      
    #include <cstdio>
    #include <iostream>
    
    using namespace std;
    
    int n;
    int a[10]={0},vi[10]={0};
    
    void write()
    {
        for (int i=1;i<=n;i++)
          printf("%5d",a[i]);
        printf("
    ");
    }
    
    void search(int i)
    {
      if (i!=n+1)
      {
        for (int j=1;j<=n;j++)
          if (vi[j]==0)
          {
              a[i]=j;
              vi[j]=1;
              search(i+1);
              vi[j]=0;
          }
      }
      else write();    
    }
    
    
    int main()
    {
        scanf("%d",&n);
        search(1);
        return 0;
    }
    View Code

    填数类:

      八皇后:https://www.luogu.org/problemnew/show/P1219

      题意概述:非常经典的题目,就不说了。

      
    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int n,sum=0;
    int heng[15]={0},zxtys[30]={0},zstyx[30]={0},v[15]={0};
    
    void write()
    {
        printf("%d",heng[1]);
        for (int i=2;i<=n;i++)
          printf(" %d",heng[i]);
        printf("
    ");
    }
    
    void search(int x)
    {
        if (x!=n+1)
            for (int i=1;i<=n;i++)
              if (v[i]==0&&zxtys[i+x]==0&&(zstyx[i-x+15])==0)
              {
                  heng[x]=i;
                  v[i]=1;
                  zxtys[i+x]=zstyx[i-x+15]=1;
                  search(x+1);
                  heng[x]=0;
                  v[i]=0;
                  zxtys[i+x]=zstyx[i-x+15]=0;
              } else ;
        else 
        {
            if (sum<=2) write();
            sum++;
        }          
    }
    
    
    int main()
    {
        scanf("%d",&n);
        search(1);
        printf("%d",sum);    
        return 0;
    }
    View Code

      数独:https://www.luogu.org/problemnew/show/P1784

      题意概述:。。。填数独。

      读入后保存每个0位的位置,搜索即可,因为题目保证了不会无解或多解,所以搜到一个解就直接输出,退出。

      
    # include <cstdio>
    # include <iostream>
    # define R register int
    
    using namespace std;
    
    int a[10][10],q;
    int ans=0;
    int h=0,x[100],y[100];
    int r[10],c[10],s[3][3];
    
    void write()
    {
        for (R i=1;i<=9;i++)
        {
            for (R j=1;j<=9;j++)
                printf("%d ",a[i][j]);
            printf("
    ");
        }
        return ;
    }
    
    void dfs(int ra)
    {
        if(ra==0)
        {
            write();
        }
        else
        {
            for (R i=1;i<=9;i++)
            {
                if(r[ x[ra] ]&(1<<i)) continue;
                if(c[ y[ra] ]&(1<<i)) continue;
                if(s[ (x[ra]-1)/3 ][ (y[ra]-1)/3 ]&(1<<i)) continue;
                
                r[ x[ra] ]+=(1<<i);
                c[ y[ra] ]+=(1<<i);
                s[ (x[ra]-1)/3 ][ (y[ra]-1)/3 ]+=(1<<i);
                a[x[ra]][y[ra]]=i;
                
                dfs(ra-1);
                
                r[ x[ra] ]-=(1<<i);
                c[ y[ra] ]-=(1<<i);
                s[ (x[ra]-1)/3 ][ (y[ra]-1)/3 ]-=(1<<i);
                a[x[ra]][y[ra]]=0;
            }
        }
    }
    
    int main()
    {
        for (R i=1;i<=9;i++)
            for (R j=1;j<=9;j++)
            {
                scanf("%d",&q);    
                a[i][j]=q;
                if(q==0) x[++h]=i,y[h]=j;
                else
                {
                    r[i]|=(1<<q);
                    c[j]|=(1<<q);
                    s[(i-1)/3][(j-1)/3]|=(1<<q);
                }
            }
        dfs(h);
        return 0;
    }
    View Code

      

      靶形数独:https://www.luogu.org/problemnew/show/P1074

      题意概述:填数独且每个位置有一个权值,数独的得分为权值*填的数,最大化得分。

      纯搜索就没什么好说的了,主要看看剪枝:1.如果剩下位置全填9且全赋9的权值还是比最优解小,退出。(这个剪枝可以写的很强,比如每个位置按照真实权值来算,同一个行不能都按9来做等,但是这样写很麻烦,消耗的时间也不一定划算,所以就弱弱的只加了一句(h-ra+1)*90+Sum<ans,效果也的确不怎么样。对于最难的数据,加不加这句剪枝效率只差了100-200ms。看到这你一定想,交上去看看吧!于是就交了上去,竟然得了80分。采取了一贯很有效的优化:顺着搜超时那就倒着搜。。。神奇的是得了95分。。。

      卡时!过了。。。

      这就很神奇了,然而卡时毕竟不是很光明正大的方法,想一想怎么优化。想一下自己玩数独的时候是怎么玩的,是不是如果某一行只有一两个数没填就先去填它们,所以预处理出每一行还没填的格子数,排序后重构搜索顺序,快到飞起。

      事实上也应该处理每一列,每一宫的格子数,动态更新之后选择填哪一个,之前还听说过从中心往边缘搜这样的优化,不过有的优化其实是有负作用的。。。

      
    // luogu-judger-enable-o2
    # include <cstdio>
    # include <iostream>
    # define R register int
    
    using namespace std;
    
    int a[10][10],q;
    int ans=0;
    int h=0,x[100],y[100];
    int f[10][10];
    int r[10],c[10],s[3][3];
    
    void dfs(int ra,int Sum)
    {
        if(ra==h+1)
            ans=max(ans,Sum);
        else
            for (R i=1;i<=9;i++)
            {
                if(r[ x[ra] ]&(1<<i)) continue;
                if(c[ y[ra] ]&(1<<i)) continue;
                if(s[ (x[ra]-1)/3 ][ (y[ra]-1)/3 ]&(1<<i)) continue;
                if((h-ra+1)*100+Sum<ans) continue;
                r[ x[ra] ]+=(1<<i);
                c[ y[ra] ]+=(1<<i);
                s[ (x[ra]-1)/3 ][ (y[ra]-1)/3 ]+=(1<<i);
                a[x[ra]][y[ra]]=i;
                
                dfs(ra+1,Sum+f[ x[ra] ][ y[ra] ]*i);
                
                r[ x[ra] ]-=(1<<i);
                c[ y[ra] ]-=(1<<i);
                s[ (x[ra]-1)/3 ][ (y[ra]-1)/3 ]-=(1<<i);
                a[x[ra]][y[ra]]=0;
            }
    }
    
    int main()
    {
        int ss[10]={0},su=0;
        for (R i=1;i<=9;i++)
            for (R j=1;j<=9;j++)
            {
                if(i==1||i==9||j==1||j==9) f[i][j]=6;
                if(((i==2||i==8)&&(j>=2&&j<=8))||((i>=2&&i<=8)&&(j==2||j==8))) f[i][j]=7;
                if(((i==3||i==7)&&(j>=3&&j<=7))||((i>=3&&i<=7)&&(j==3||j==7))) f[i][j]=8;
                if(((i==4||i==6)&&(j>=4&&j<=6))||((i>=4&&i<=6)&&(j==4||j==6))) f[i][j]=9;
                if(i==5&&j==5)                f[i][j]=10;
            }
        for (R i=1;i<=9;i++)
            for (R j=1;j<=9;j++)
            {
                scanf("%d",&q);    
                a[i][j]=q;
                if(q!=0)
                {
                    if(r[i]&(1<<q))
                    {
                        printf("-1");
                        return 0;
                    }
                    if(c[j]&(1<<q))
                    {
                        printf("-1");
                        return 0;
                    }
                    if(s[(i-1)/3][(j-1)/3]&(1<<q))
                    {
                        printf("-1");
                        return 0;    
                    }
                    r[i]|=(1<<q);
                    c[j]|=(1<<q);
                    s[(i-1)/3][(j-1)/3]|=(1<<q);
                    su+=f[i][j]*q;
                }
            }
        for (int i=1;i<=9;i++)
            for (int j=1;j<=9;j++)
                if(a[i][j]==0) ss[i]++;
        int mx=0,my=10000;
        for (int i=1;i<=9;i++)
        {
            for (int j=1;j<=9;j++)
                if(ss[j]<my)  { my=ss[j]; mx=j; }
            ss[mx]=100000000;
            my=10000000;
            for (int z=1;z<=9;z++)
                if(a[mx][z]==0) x[++h]=mx,y[h]=z;
        }
        dfs(1,su);
        if(ans==0)
            printf("-1");
        else
            printf("%d",ans);
        return 0;
    }
    View Code

      八数码难题:https://www.luogu.org/problemnew/show/P1379

      题意概述:九个格子中有8个数字,一个空格,每次将空格向上下左右移动,求达到目标状态的最小步数。

      这里写的是简单版,跑的挺慢的,后来又写了双向bfs,扔到“搜索-2”里了。

      没有用康拓展开,而是暴力展成链以后扔进set里面。

      
    # include <iostream>
    # include <set>
    # include <queue>
    # include <cstdio>
    
    using namespace std;
    
    const int dx[]={-1,0,0,1};
    const int dy[]={0,1,-1,0};
    
    int x,dream=123804765;
    int ans=-1;
    int q[10000000]={0};
    int ql[10000000]={0};
    set<int> s;
    
    int A()
    {
        int head=0,tail=1;
        while (1)
        {
            int il=0,jl=0,n[3][3],now;
            now=q[++head];
            if(now==dream)
                return ql[head];
            for (register int i=2;i>=0;i--)
                  for (register int j=2;j>=0;j--)
                {
                      if(now%10==0) il=i,jl=j;
                      n[i][j]=now%10;
                      now=now/10;
                }
            for (register int i=0;i<4;i++)
            {
                int xx=il+dx[i];
                int yy=jl+dy[i];
                if(xx>=0&&xx<=2&&yy>=0&&yy<=2)
                {
                    int nn[3][3];
                    for (register int ii=0;ii<3;ii++)
                      for (register int jj=0;jj<3;jj++) nn[ii][jj]=n[ii][jj];
                    swap(nn[il][jl],nn[xx][yy]);
                    now=nn[0][0]*100000000+nn[0][1]*10000000+nn[0][2]*1000000+nn[1][0]*100000+nn[1][1]*10000+nn[1][2]*1000+nn[2][0]*100+nn[2][1]*10+nn[2][2];
                    if(s.find(now)!=s.end()) 
                    { continue; }
                    else
                    {
                        s.insert(now);
                        q[++tail]=now;
                        ql[tail]=ql[head]+1;
                    }
                }
            }
        }
    }
    
    int main()
    {
        scanf("%d",&x);
        q[1]=x;
        ql[1]=0;
        s.insert(x);
        ans=A();
        printf("%d",ans);
        return 0;
    }
    View Code

      是的。。。什么优化也没有,用了set还没开O2,就这样都能过。。。

      

      聪明的打字员:https://www.luogu.org/problemnew/show/P1949

      题意概述:不想概述$qwq$.

      可以考虑双向广搜,不过记忆化一下就足够了.

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <queue>
     4 # define R register int
     5 
     6 using namespace std;
     7 
     8 int y,m,step,n,a[10],po,neww,Min=9,Max;
     9 bool vis[10000006];
    10 queue <int> q,b;
    11 
    12 int bfs ()
    13 {
    14     int bef;
    15     q.push(y * 10 + 1);
    16     b.push(0);
    17     vis[y*10+1]=true;
    18     while (q.size())
    19     {
    20         n = q.front();
    21         step = b.front();
    22         q.pop();
    23         b.pop();
    24         po = n % 10;
    25         n /= 10;
    26         if(n==m) return step;
    27 
    28         bef=n;
    29         for (R i = 6; i >= 1; --i)
    30             a[i] = n % 10, n /= 10;
    31 
    32         swap(a[1], a[po]);
    33         neww = 0;
    34         for (R i = 1; i <= 6; ++i)
    35             neww = neww * 10 + a[i];
    36         if (!vis[neww * 10 + po])
    37             vis[neww * 10 + po] = true, q.push(neww * 10 + po), b.push(step + 1);
    38         swap(a[1], a[po]);
    39 
    40         swap(a[6], a[po]);
    41         neww = 0;
    42         for (R i = 1; i <= 6; ++i)
    43             neww = neww * 10 + a[i];
    44         if (!vis[neww * 10 + po])
    45             vis[neww * 10 + po] = true, q.push(neww * 10 + po), b.push(step + 1);
    46         swap(a[6], a[po]);
    47 
    48         if(po!=6)
    49         {
    50             po++;
    51             neww = bef;
    52             if (po <= 6 && vis[neww * 10 + po] == false)
    53                 vis[neww * 10 + po] = true, q.push(neww * 10 + po), b.push(step + 1);
    54             po--;
    55         }
    56         
    57         if(po!=1)
    58         {
    59             po--;
    60             neww = bef;
    61             if (po >= 1 && vis[neww * 10 + po] == false)
    62                 vis[neww*10 + po] = true, q.push(neww * 10 + po), b.push(step + 1);
    63             po++;
    64         }
    65         
    66         if(a[po]+1<=Max)
    67         {
    68             a[po]++;
    69             neww = 0;
    70             for (R i = 1; i <= 6; ++i)
    71                 neww = neww * 10 + a[i];
    72             if (!vis[neww * 10 + po])
    73                 vis[neww * 10 + po] = true, q.push(neww * 10 + po), b.push(step + 1);
    74             a[po]--;
    75         }
    76         
    77         if(a[po]-1>=Min)
    78         {
    79             a[po]--;
    80             neww = 0;
    81             for (R i = 1; i <= 6; ++i)
    82                 neww = neww * 10 + a[i];
    83             if (!vis[neww * 10 + po])
    84                 vis[neww * 10 + po] = true, q.push(neww * 10 + po), b.push(step + 1);
    85             a[po]++;
    86         }
    87     }
    88     return -1;
    89 }
    90 
    91 int main()
    92 {
    93     scanf("%d%d",&y,&m);
    94     int t=m;
    95     for (R i=1;i<=6;++i)
    96         Min=min(Min,t%10),Max=max(Max,t%10),t/=10;
    97     printf("%d",bfs());
    98     return 0;
    99 }
    聪明的打字员

      

      魔板:https://www.luogu.org/problemnew/show/P2730

      题意概述:一块$2*4$的板子,三种操作,求开始状态到目标状态的最小步数.

      做多了发现这种题都是套路,状压之后模拟每个操作就好了.

      
      1 // luogu-judger-enable-o2
      2 # include <cstdio>
      3 # include <iostream>
      4 # include <map>
      5 # include <algorithm>
      6 # define R register int
      7 
      8 using namespace std;
      9 
     10 int cs,beg,ne,q[41000],h,t,b[41000],c[41000];
     11 int ch[3][5],a[3][5],x,sta[1000],gol,pre[41000],vis[41000];
     12 map <int,int> m1; //p->id
     13 map <int,int> m2; //id->p
     14 
     15 void init()
     16 {
     17     int a[10],x,h=1;
     18     for (R i=1;i<=8;++i)
     19         a[i]=i;
     20     m1[12345678]=1;
     21     m2[1]=12345678;
     22     while (next_permutation(a+1,a+9))
     23     {
     24         x=0;
     25         for (R i=1;i<=8;++i)
     26             x=x*10+a[i];
     27         m1[x]=++h;
     28         m2[h]=x;
     29     }
     30 }
     31 
     32 void giv()
     33 {
     34     for (R i=1;i<=2;++i)
     35         for (R j=1;j<=4;++j)
     36             ch[i][j]=a[i][j];
     37 }
     38 
     39 int pul()
     40 {
     41     int x=0;
     42     for (R i=1;i<=4;++i)
     43         x=x*10+ch[1][i];
     44     for (R i=4;i>=1;--i)
     45         x=x*10+ch[2][i];
     46     return x;
     47 }
     48 
     49 void in_que (int x,int y)
     50 {
     51     vis[x]=true;
     52     q[++t]=x;
     53     b[t]=b[h]+1;
     54     c[x]=y;
     55     pre[x]=q[h];
     56 }
     57 
     58 int bfs()
     59 {
     60     beg=m1[12345678];
     61     q[1]=beg;
     62     b[1]=0;
     63     h=t=1;
     64     vis[1]=true;
     65     while (h<=t)
     66     {
     67         beg=m2[ q[h] ];
     68         if(q[h]==gol) 
     69             return b[h];
     70         for (R j=1;j<=4;++j)
     71             a[2][j]=beg%10,beg/=10;
     72         for (R j=4;j>=1;--j)
     73             a[1][j]=beg%10,beg/=10;
     74         giv();
     75         for (R i=1;i<=4;++i)
     76             swap(ch[1][i],ch[2][i]);
     77         ne=m1[pul()];
     78         if(!vis[ne]) in_que(ne,1);
     79         giv();
     80         for (R i=4;i>=2;--i)
     81             for (R j=1;j<=2;++j)
     82                 swap(ch[j][i-1],ch[j][i]);
     83         ne=m1[pul()];
     84         if(!vis[ne]) in_que(ne,2);
     85         giv();
     86         swap(ch[1][2],ch[1][3]);
     87         swap(ch[2][2],ch[2][3]);
     88         swap(ch[1][2],ch[2][3]);
     89         ne=m1[pul()];
     90         if(!vis[ne]) in_que(ne,3);    
     91         h++;
     92     }
     93     return -1;
     94 }
     95 
     96 int main()
     97 {
     98     init();
     99     for (R i=1;i<=8;++i)
    100         scanf("%d",&x),gol=gol*10+x;
    101     gol=m1[gol];
    102     printf("%d
    ",bfs());
    103     int Top=0,tot=0;
    104     for (R i=gol;i!=1;i=pre[i])
    105         sta[++Top]=c[i];
    106     for (R i=Top;i>=1;--i)
    107     {
    108         tot++;
    109         printf("%c",sta[i]+'A'-1);
    110         if(tot%60==0) printf("
    ");
    111     }
    112     return 0;
    113 }
    魔板

    网格图类:

      棋盘:https://www.luogu.org/problemnew/show/P3956

      题目概述:这道题永远不会忘。

      考场上为什么写不出来啊啊啊啊。

      
    # include <cstdio>
    # include <iostream>
    # include <cstring>
    # include <string>
    
    using namespace std;
    
    int m,n,c,x,y,ans=-1;
    bool F=false;
    short G[105][105];
    int Min[105][105][2];
    bool vis[105][105];
    
    const int dx[]={-1,0,0,1},dy[]={0,-1,1,0};
    
    void dfs(int x,int y,bool can,int sc)
    {
        if(x==m&&y==m)
        {
            if(ans==-1) ans=sc;
            ans=min(sc,ans);
            return ;
        }
        for (int i=0;i<4;i++)
        {
            int ax=x+dx[i];
            int ay=y+dy[i];
            if (vis[ax][ay]) continue;
            if (ax<1||ax>m||ay<1||ay>m)  continue;
            if (can==false&&G[ax][ay]==0) continue;
            if(G[ax][ay]==0)
            {
                G[ax][ay]=G[x][y];
                vis[ax][ay]=true;
                if(sc+2<Min[ax][ay][1])
                {
                    Min[ax][ay][1]=sc+2;
                    dfs(ax,ay,false,sc+2);
                }
                vis[ax][ay]=false;
                G[ax][ay]=0;
                continue;
            }
            vis[ax][ay]=true;
            if(G[x][y]==G[ax][ay])
            {
                if(Min[ax][ay][0]>sc)
                {
                    Min[ax][ay][0]=sc;
                    dfs(ax,ay,true,sc);
                }
            }
            else
            {
                if(Min[ax][ay][0]>sc+1)
                {
                    Min[ax][ay][0]=sc+1;
                    dfs(ax,ay,true,sc+1);
                }
            }
            vis[ax][ay]=false;
        }
    }
    
    int main()
    {
        memset(Min,1,sizeof(Min));
        scanf("%d%d",&m,&n);
        if(m==1)
        {
            printf("0");
            return 0;
        }
        for (int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&x,&y,&c);
            if (c==0) G[x][y]=1;
            if (c==1) G[x][y]=2;
        }
        vis[1][1]=true;
        dfs(1,1,true,0);//1,1点;可变色,金币花费0 
        printf("%d",ans);
        return 0;
    } 
    View Code

    不知道该怎么归类类:

      数字三角形:https://www.luogu.org/problemnew/show/P1118

      题意概述:1-n的排列中两两相加得到n-1个数,不断进行这个过程直到剩下一个数,给出这个数,求原排列。

      朴素搜索大概会T,所以要加可行性剪枝,但是在全部算完后怎么知道这个数的贡献是多少呢?很巧妙,事实上每个数的权值正是当行的杨辉三角。

      
    # include <cstdio>
    # include <iostream>
    
    using namespace std;
    
    int n,sum;
    bool f=false;
    int ans[15]={0};
    int pascal[14][14];
    bool vis[14]={0};
    
    void writ()
    {
        for (int i=1;i<n;i++)
          printf("%d ",ans[i]);
        printf("%d",ans[n]);
        f=true;
    }
    
    void dfs(int x,int s)
    {
        if(s==sum&&x==n+1)
          writ();
        if(f) return ;
        if(s>=sum) return ;
        for (int i=1;i<=n;i++)
        {
            if(vis[i]) continue;
            vis[i]=true;
            ans[x]=i;
            dfs(x+1,s+i*pascal[n][x]);
            vis[i]=false;
            ans[x]=0;
        }    
        return ;
    }
    
    int main()
    {
        pascal[1][1]=1;
        scanf("%d%d",&n,&sum);
        for (int i=1;i<=n;i++)
          for (int j=1;j<=i;j++)
            if(i==1&&j==1) continue; else pascal[i][j]=pascal[i-1][j]+pascal[i-1][j-1];
        dfs(1,0);
        return 0;
    }
    View Code

      砝码称重:https://www.luogu.org/problemnew/show/P1441

      题意概述:从n个数中去掉m个数,求剩下的数能拼成的最大连续价值。(1——max都可以拼出来)

      看起来是道蓝题挺吓人的,其实简单到不可思议。。。

      先写一个朴素搜索,爆搜取哪些数,然后做01背包,得了60;

      又卡了好久,不知道怎么优化,后来加了一句迷之剪枝竟然过了。。。因为是求数的组合,所以按照组合数那道题的思路优化。有的题目看似简单,其中的思想化用后却可以解决更难的问题。

      总结出一个不算经验的搜索经验来:如果是选出一些元素且元素的顺序不影响结果时,应在dfs中加入上一层搜到的数在数组中的排名,直接从这里往后搜就好了,防止搜出重复的状态。

      
    # include <cstdio>
    # include <iostream>
    # include <cstring>
    # define R register int
    
    using namespace std;
    
    int n,m,S;
    int ans=0,a[25];
    bool vis[25];
    bool dp[20009];
    
    void check()
    {
        memset(dp,0,sizeof(dp));
        dp[0]=true;
        for (R i=1;i<=n;i++)
        {
            if(vis[i]) continue;
            for (R j=S;j>=a[i];j--)
                dp[j]|=dp[j-a[i]];
        }
        int anss=0;
        for (R i=1;i<=S;i++)
            if(dp[i]) anss++;
        ans=max(ans,anss);
    }
    
    void dfs(int x,int las)
    {
        if(x==m)
        {
            check();
            return ;
        }
        for (R i=las;i<=n;i++)
        {
            if(vis[i]) continue;
            vis[i]=true;
            dfs(x+1,i+1);
            vis[i]=false;
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (R i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            S+=a[i];    
        }
        dfs(0,1);
        printf("%d",ans);
        return 0;
    }
    View Code

      邮票面值设计:https://www.luogu.org/problemnew/show/P1021

      题意概述:找出k个数,从中取出n个(可以重复),求最大连续价值。

      一开始写了搜索of搜索,先搜取哪些数,再搜可以拼成的所有数。。。数据很水,竟然得了75分。

      后来改成了搜索of dp。用到了一个性质:如果用一些小的数不能拼出X这个数字,加上一些比X大的数自然更拼不出来。所以在dfs时保存当前能拼出的连续的最大的数,选下一个数时maxn+1就是上界。

      怎么判断maxn呢?这里其实挺暴力的,而且也不是很快,但是相比无尽的搜索还是快了很多,即每加入一个新的数就重新做一次dp,dp[i][j]表示从前i个数中拼出j所需数的最小个数,当然可以减掉i这一维啦。

      
    # include <cstdio>
    # include <iostream>
    # include <cstring>
    # define R register int
    
    using namespace std;
    
    int n,k,ans;
    int a[20];
    int shzr[20];
    
    int dp(int x,int maxn)
    {
        int f[50009];
        memset(f,1,sizeof(f));
        f[0]=0;
        for (R i=1;i<=x;i++)
            for (R j=a[i];j<=a[i]*n;j++)
                f[j]=min(f[j],f[j-a[i]]+1);
        int Tot=1;
        while (f[Tot]<=n) Tot++;
        return Tot-1;
    }
    
    void dfs1(int x,int las,int maxn)
    {
        if(x==k+1)
        {
            if(maxn>ans)
            {
                ans=maxn;
                for (R i=1;i<=k;i++)
                    shzr[i]=a[i];
            }
            return ;
        }
        for (R i=las;i<=maxn+1;i++)
        {
            a[x]=i;
            dfs1(x+1,i+1,dp(x,maxn));
            a[x]=0;
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        dfs1(1,1,0);
        for (int i=1;i<=k;i++)
            printf("%d ",shzr[i]);
        printf("
    MAX=%d",ans);
        return 0;
    }
    View Code

    计算几何类:其实也不能算是计算几何,可是也不知道该怎么归类。

      油滴扩展:https://www.luogu.org/problemnew/show/P1378

      题意概述:在一个方框中滴一些油滴,合理安排滴的顺序,使得占据的总面积最大。(油滴数量<=6)

      搜索判断顺序即可,对于每一滴油,判断是否被框或者是已有的油滴拦住来判断半径,就做完啦!注意一些细节:有可能旧的油滴已经覆盖了新油滴的起点,这时候要将新油滴的半径设置为0;其实这道题真正的难点在于背圆周率?。如果设置为$3.14159$,就只有70分,如果是$3.1415926$,就可以拿到满分。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cmath>
     4 
     5 using namespace std;
     6 
     7 const double pi=3.1415926;
     8 int n,x_1,x_2,y_1,y_2,S;
     9 double ans,r[7];
    10 int x[7],y[7];
    11 bool vis[7];
    12 
    13 void dfs(int a,double Su)
    14 {
    15     if(a==n+1)
    16     {
    17         ans=max(ans,Su);
    18         return ;
    19     }
    20     for (int i=1;i<=n;++i)
    21     {
    22         if(vis[i]) continue;
    23         vis[i]=true;
    24         r[i]=10001;
    25         r[i]=min(r[i],(double)x[i]-x_1);
    26         r[i]=min(r[i],(double)x_2-x[i]);
    27         r[i]=min(r[i],(double)y_2-y[i]);
    28         r[i]=min(r[i],(double)y[i]-y_1);
    29         for (int j=1;j<=n;++j)
    30         {
    31             if(i==j) continue;
    32             if(vis[j]==false) continue;
    33             r[i]=min(r[i],max((double)sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))-r[j],0.0));
    34         }
    35         dfs(a+1,Su+pi*r[i]*r[i]);
    36         vis[i]=false;
    37     }
    38 }
    39 
    40 int main()
    41 {
    42     scanf("%d",&n);
    43     scanf("%d%d%d%d",&x_1,&y_1,&x_2,&y_2);
    44     if(x_1>x_2) swap(x_1,x_2);
    45     if(y_1>y_2) swap(y_1,y_2);
    46     S=(x_2-x_1)*(y_2-y_1);
    47     for (int i=1;i<=n;i++)
    48         scanf("%d%d",&x[i],&y[i]);
    49     dfs(1,0);
    50     printf("%.0lf",(double)S-ans);
    51     return 0;
    52 }
    View Code

    字符串类:

      最近发现字符串类的搜索考的还是比较多的。

      还有一件事就是很多题会忘记写总结,但是看总结还是比较频繁的,所以把想写却还没写的题目链接摆在这里。

      统计单词个数:https://www.luogu.org/problemnew/show/P1026

      字串变换:https://www.luogu.org/problemnew/show/P1032

      单词接龙:https://www.luogu.org/problemnew/show/P1019

      好像也就这些了吧...看到这里如果您想到了什么别的字符串搜索题,欢迎告诉我。

  • 相关阅读:
    清除浮动的三种方式
    js控制滚动条默认在底部
    npm 基础命令
    package.json中^,~的区别
    mac 使用iTerm2快捷登录远程服务器
    iterm 分屏切换快捷键与配色设置
    git tag — 标签相关操作
    gulp iconfont
    webpack 3 升级 webpack4 个人笔记
    import * as x from 'xx' 和 import x from 'xx'
  • 原文地址:https://www.cnblogs.com/shzr/p/9064787.html
Copyright © 2020-2023  润新知