搜索-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("\n");
}

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("\n");
    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("\n");
}

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("\n");
}

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("\n");
    }
    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\n",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("\n");
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("\nMAX=%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

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

 

转载于:https://www.cnblogs.com/shzr/p/9064787.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值