搜索专题 题解

一开始写在了word上……后来搬运了过来

格式乱凑合看QAQ

搜索专题 题解

A.文化之旅

题面有歧义的大水题,不谈。无限qj测试点

B.寻找道路

建反图跑dfs处理与终点连通性,最短路加特判水题不谈。

C. 靶形数独

预处理belong[]表示属于哪个九宫格,sc[]表示分数

按行列搜会当场T飞,so每次遍历全图找到能填数最少的点进行搜索

三个条件需同时满足可以二进制优化,然而我卡了过去。

 

 

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int sc[12][12],vf[12][12],vh[12][12],vl[12][12],ans=-1,a[12][12],belong[12][12];
int num0,nowx,nowy,score0;
void pre()
{
    int l=1,r=9;
    while(l<=r)
    {
        for(int i=l;i<=r;i++)
            sc[l][i]=sc[r][i]=sc[i][l]=sc[i][r]=l+5;
        l++;r--;
    }    
    belong[1][1]=1;
    belong[1][2]=1;
    belong[1][3]=1;
    belong[1][4]=2;
    belong[1][5]=2;
    belong[1][6]=2;
    belong[1][7]=3;
    belong[1][8]=3;
    belong[1][9]=3;
    belong[2][1]=1;
    belong[2][2]=1;
    belong[2][3]=1;
    belong[2][4]=2;
    belong[2][5]=2;
    belong[2][6]=2;
    belong[2][7]=3;
    belong[2][8]=3;
    belong[2][9]=3;
    belong[3][1]=1;
    belong[3][2]=1;
    belong[3][3]=1;
    belong[3][4]=2;
    belong[3][5]=2;
    belong[3][6]=2;
    belong[3][7]=3;
    belong[3][8]=3;
    belong[3][9]=3;
    belong[4][1]=4;
    belong[4][2]=4;
    belong[4][3]=4;
    belong[4][4]=5;
    belong[4][5]=5;
    belong[4][6]=5;
    belong[4][7]=6;
    belong[4][8]=6;
    belong[4][9]=6;
    belong[5][1]=4;
    belong[5][2]=4;
    belong[5][3]=4;
    belong[5][4]=5;
    belong[5][5]=5;
    belong[5][6]=5;
    belong[5][7]=6;
    belong[5][8]=6;
    belong[5][9]=6;
    belong[6][1]=4;
    belong[6][2]=4;
    belong[6][3]=4;
    belong[6][4]=5;
    belong[6][5]=5;
    belong[6][6]=5;
    belong[6][7]=6;
    belong[6][8]=6;
    belong[6][9]=6;
    belong[7][1]=7;
    belong[7][2]=7;
    belong[7][3]=7;
    belong[7][4]=8;
    belong[7][5]=8;
    belong[7][6]=8;
    belong[7][7]=9;
    belong[7][8]=9;
    belong[7][9]=9;
    belong[8][1]=7;
    belong[8][2]=7;
    belong[8][3]=7;
    belong[8][4]=8;
    belong[8][5]=8;
    belong[8][6]=8;
    belong[8][7]=9;
    belong[8][8]=9;
    belong[8][9]=9;
    belong[9][1]=7;
    belong[9][2]=7;
    belong[9][3]=7;
    belong[9][4]=8;
    belong[9][5]=8;
    belong[9][6]=8;
    belong[9][7]=9;
    belong[9][8]=9;
    belong[9][9]=9;
}
/*int belong(int hang,int lie)
{
    if(hang>3)hang=(hang%3?hang/3:hang/3-1);
    else hang=0;
    if(lie>3)lie=(lie%3?lie/3:lie/3-1);
    else lie=0;
    return hang*3+lie+1;
}*/
int cacl()
{
    int tot=0;
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)tot+=a[i][j]*sc[i][j];
    return tot;
}
bool judge(int i,int j,int num)
{
    if(vh[i][num]||vl[j][num]||vf[belong[i][j]][num])return 0;
    return 1;
}
void findloc()
{
    nowx=nowy=0;
    int minx=0x3f3f3f3f;
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
        {
            if(a[i][j])continue;
            int res=0;
            for(int num=1;num<=9;num++)
            {
                if(!judge(i,j,num))continue;
                res++;
            }
            if(res<minx)minx=res,nowx=i,nowy=j;
        }    
}
void dfs(int hang,int lie)
{    
    for(int num=1;num<=9;num++)
    {
        if(!judge(hang,lie,num))continue;
        vh[hang][num]=vl[lie][num]=vf[belong[hang][lie]][num]=1;
        a[hang][lie]=num;
        findloc();
        if(nowx==0||nowy==0)
            ans=max(ans,cacl());    
        else dfs(nowx,nowy);
        vh[hang][num]=vl[lie][num]=vf[belong[hang][lie]][num]=0;
        a[hang][lie]=0;
    }
    return ;
}
int main()
{
    pre();
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
        {
            int x;
            scanf("%d",&x);
            a[i][j]=x;
            if(x)vh[i][x]=vl[j][x]=vf[belong[i][j]][x]=1,score0+=sc[i][j]*x;
        }
    findloc();
    dfs(nowx,nowy);
    cout<<ans<<endl;
    return 0;
}
View Code

 

 

 

 D.饮水入城

首先跑bfs/dfs预处理每个湖城能引水到哪个沙城(事实证明后者效率会把前者虐出翔)  可以状压处理|在一起判出0的情况

之后的东西就比较玄学(我赶脚是物理原理?)

这里因为懒就直接粘miku的题解

 

仔细看看题里给的图,我们可以发现如下定理

①:若在所以临湖城市都建蓄水站,如果没能覆盖所有沙漠城市,则剩下的城市就是不能覆盖的城市

②:若能完成覆盖,则每个点一定能覆盖一段城市

 

 

③:一个点能覆盖的最左城市不可能超过它左边的点能覆盖的最左城市(一个城市也不能覆盖的点除外)

 

 

这个性质发现之后整道题迎刃而解

%%%mikufunTQL

之后转化成了区间最小覆盖问题 记录左右端点dp解决

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<bitset>
using namespace std;
const int N=510;
const int dx[5]={0,1,0,-1,0},
          dy[5]={0,0,1,0,-1};
bitset<501> v[N];          
int n,m;
int h[N][N],s[N][N],f[N];
struct seg
{
    int l,r;
    friend bool operator < (seg a1,seg a2)
    {
        return a1.l==a1.r?a1.r<a2.r:a1.l<a2.l;
    }
}a[N];
queue<int> x,y;
int read()
{
    int xx=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')
        {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')
        {xx=xx*10+ch-'0';ch=getchar();}
    return xx*f;
}
void bfs(int xs,int ys)
{
    memset(s,0,sizeof(s));
    x.push(xs);y.push(ys);
    while(!x.empty())
    {
        for(int i=1;i<=4;i++)
        {
            int nowx=x.front(),nxtx=nowx+dx[i],nowy=y.front(),nxty=nowy+dy[i];
            if(h[nxtx][nxty]>=h[nowx][nowy]||s[nxtx][nxty]||nxtx==0||nxty==0||nxtx>n||nxty>m)continue;
            s[nxtx][nxty]=1;
            x.push(nxtx);y.push(nxty);
        }
        x.pop();y.pop();
    }
    for(int i=1;i<=m;i++)
        if(s[n][i])v[ys][i]=1;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)h[i][j]=read();
    for(int i=1;i<=m;i++)bfs(1,i);
    bitset<501> all;all=v[1];int cnt0=0;
    for(int i=2;i<=m;i++)all|=v[i];
    for(int i=1;i<=m;i++)
    {
        if(!all[i])cnt0++;
        for(int j=1;j<=m;j++)
            if(v[i][j])
            {
                a[i].l=j;
                while(v[i][j])j++;
                a[i].r=j-1;
                break;
            }
    }    
    if(cnt0)
    {
        if(n==1)puts("1");
        else puts("0");
        cout<<cnt0<<endl;
        return 0;
    }
    puts("1");
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
            if(a[j].l<=i&&a[j].r>=i)
            f[i]=min(f[i],f[a[j].l-1]+1);
    cout<<f[m]<<endl;
    return 0;
} 
View Code

 

 E.传染病控制

水题,只要不想成树dp一切好说

(然后我就不好说了)

从上往下每层砍条边,使留下的节点最少

预处理一下直接按深度暴搜(打标记/取消)

建图的时候父子关系不明是坑点Orz

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=310;
int to[N<<1],nxt[N<<1],head[N],tot=0,d[N<<1];
int n,m,ans=0x3f3f3f3f;
int size[N],cut[N];
vector<int> dep[N];
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')
        {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')
        {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int predfs(int x,int deep)
{
    d[x]=deep;dep[deep].push_back(x);
    for(int i=head[x];i;i=nxt[i])
        size[x]+=predfs(to[i],deep+1);
    return size[x];
}
void cutson(int x)
{
    cut[x]=1;
    for(int i=head[x];i;i=nxt[i])
        cutson(to[i]);
}
void recover(int x)
{
    cut[x]=0;
    for(int i=head[x];i;i=nxt[i])
        recover(to[i]);
}
void dfs(int deep,int now)
{
    for(int i=0;i<dep[deep+1].size();i++)
    {
        int y=dep[deep+1][i];
        if(cut[y])continue;
        cutson(y);
        dfs(deep+1,now-size[y]);
        recover(y);
    }
    ans=min(ans,now);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)size[i]=1;
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        if(x>y)swap(x,y);
        add(x,y);
    }
    predfs(1,1);
    dfs(1,n);
    cout<<ans<<endl;
    return 0;
}
View Code

 

 

 F.虫食算

这道题思考比较充分,打的时候思路也比较清晰

(然而还是没有1A  水到T90)

加一个随时检查当前填数情况是否完全合法的函数来剪枝

然后按列往前搜就完事了

 

 

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,num[31],rev[31];
char a[5][31];
int mod(int x)
{
    return x>=n?x-n:x;
}
int t(char x)
{
    return (int)(x-'A');
}
void print()
{
    for(int i=0;i<n;i++)printf("%d ",num[i]);
}
void ini()
{
    for(int i=0;i<=n;i++)num[i]=rev[i]=-1;
}
bool judge()
{
    for(int i=n;i>=1;i--)
    {
        if(num[t(a[1][i])]==-1||num[t(a[2][i])]==-1||num[t(a[3][i])]==-1)continue;
        int a1=num[t(a[1][i])],a2=num[t(a[2][i])],sum=num[t(a[3][i])];
        if(mod(a1+a2)!=sum&&mod(a1+a2+1)!=sum)return 0;
    }
    return 1;
}
bool dfs(int now,int line,int jw)
{    
    /*cout<<endl<<now<<' '<<line<<endl;
    for(int i=0;i<n;i++)cout<<num[i]<<' ';
    cout<<endl;*/
    if(line==3)
    {
        int sum=mod(num[t(a[1][now])]+num[t(a[2][now])]+jw);
        if(num[t(a[1][now])]+num[t(a[2][now])]+jw>=n)jw=1;
        else jw=0;
        if(num[t(a[3][now])]==-1)
        {
            if(rev[sum]!=-1)return 0;
            num[t(a[3][now])]=sum;
            rev[sum]=num[t(a[3][now])];
            if(now==1)
            {
                print();
                return 1;
            }
            else if(judge())dfs(now-1,1,jw);
            num[t(a[3][now])]=rev[sum]=-1;
        }
        else if(num[t(a[3][now])]==sum)
        {
            if(now==1)
            {
                print();
                return 1;
            }
            else dfs(now-1,1,jw);
        }
        else return 0;
    }
    else
    {
        if(num[t(a[line][now])]!=-1&&judge)dfs(now,line+1,jw);
        else for(int i=0;i<n;i++)
        {
            if(rev[i]!=-1)continue;
            num[t(a[line][now])]=i;
            rev[i]=num[t(a[line][now])];
            if(judge()==0)
            {
                num[t(a[line][now])]=rev[i]=-1;
                continue;
            }
            dfs(now,line+1,jw);
            num[t(a[line][now])]=rev[i]=-1;
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=3;i++)scanf("%s",a[i]+1);
    ini();
    dfs(n,1,0);
    return 0;
}
View Code

 

 

 

 

 G.斗地主

其实还是蛮水的

主要是坑点多,规则繁杂

什么2不能当顺子 对王可以带之类的

鄙蒟蒻没有预处理散牌 而是先出顺子等丢牌较快的操作 最后考虑散牌  由于单对三炸都能一下出完直接if(sum[i])num++就完事了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int c[25];
int T,n,ans=0x3f3f3f3f;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')
        {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')
        {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void dfs(int num)
{
    if(num>=ans)return ;
    
    int now=0;
    for(int i=3;i<=14;i++)
    {
        if(!c[i])now=0;
        else 
        {
            now++;
            if(now>=5)
            {
                for(int j=i;j>=i-now+1;j--)c[j]--;
                dfs(num+1);
                for(int j=i;j>=i-now+1;j--)c[j]++;
            }
        }
    }
    now=0;//cout<<"****"<<endl;
    for(int i=3;i<=14;i++)
    {
        if(c[i]<2)now=0;
        else
        {
            now++;
            if(now>=3)
            {
                for(int j=i;j>=i-now+1;j--)c[j]-=2;
                dfs(num+1);
                for(int j=i;j>=i-now+1;j--)c[j]+=2;
            }
        }
    }
    now=0;
    for(int i=3;i<=14;i++)
    {
        if(c[i]<3)now=0;
        else
        {
            now++;
            if(now>=2)
            {
                for(int j=i;j>=i-now+1;j--)c[j]-=3;
                dfs(num+1);
                for(int j=i;j>=i-now+1;j--)c[j]+=3;
            }
        }
    }
    for(int i=2;i<=14;i++)
    {
        if(c[i]<=2)continue;
        if(c[i]==3)
        {
            c[i]-=3;
            for(int j=2;j<=15;j++)
            {
                if(c[j]==0||i==j)continue;
                c[j]--;
                dfs(num+1);
                c[j]++;
            }
            for(int j=2;j<=14;j++)
            {
                if(c[j]<2||i==j)continue;
                c[j]-=2;
                dfs(num+1);
                c[j]+=2;
            }
            c[i]+=3;
        }
        else if(c[i]>3)
        {
            c[i]-=3;
            for(int j=2;j<=15;j++)
            {
                if(c[j]==0||i==j)continue;
                c[j]--;
                dfs(num+1);
                c[j]++;
            }
            for(int j=2;j<=14;j++)
            {
                if(c[j]<2||i==j)continue;
                c[j]-=2;
                dfs(num+1);
                c[j]+=2;
            }
            c[i]+=3;
            
            c[i]-=4;
            for(int j=2;j<=15;j++)
            {
                if(c[j]==0||i==j)continue;
                c[j]--;
                for(int k=2;k<=15;k++)
                {
                    if(c[k]==0||i==k||k==j)continue;
                    c[k]--;
                    dfs(num+1);
                    c[k]++;
                }
                c[j]++;
            }
            for(int j=2;j<=14;j++)
            {
                if(c[j]<2||i==j)continue;
                c[j]-=2;
                for(int k=2;k<=14;k++)
                {
                    if(c[k]<2||i==k||k==j)continue;
                    c[k]-=2;
                    dfs(num+1);
                    c[k]+=2;
                }
                c[j]+=2;
            }
            c[i]+=4;                        
        }
    }
    for(int i=2;i<=15;i++)
        if(c[i])num++;
    ans=min(ans,num);
}
void work()
{
    memset(c,0,sizeof(c));
    ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        int x=read(),tmp=read();
        if(!x)c[15]++;
        else if(x==1)c[14]++;
        else c[x]++;
    }
    dfs(0);
    printf("%d\n",ans);
}
int main()
{
    T=read();n=read();
    while(T--)work();
    return 0;
}
View Code

 

 

 

H.Mayan游戏

美妙大模拟

多写函数、码风清晰能省去不少调试的时间

剪枝:

相同颜色块直接跳过(Obviously)

固定方向移动:右边有块or左边没块  省去重复判断

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
#define rint register int
int n,ans[10][5],map[10][10],st[10][10][10];
bool no[10][10];
inline int Read()
{
    register int ret;
    register char r;
    while(r=getchar(),r<'0'||r>'9');ret=r-48;
    while(r=getchar(),r>='0'&&r<='9')ret=ret*10+r-48;
    return ret;
}
void save(int now)
{
    for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
            st[now][i][j]=map[i][j];
}
void recover(int now)
{
    for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
            map[i][j]=st[now][i][j];
}
void drop()
{
    for(rint i=1;i<=5;i++)
    {
        int x=0;
        for(rint j=1;j<=7;j++)
        {
            if(!map[i][j])x++;
            else
            {
                if(!x)continue;
                map[i][j-x]=map[i][j];
                map[i][j]=0;
            }
        }
    }
}
bool boom()
{
    rint ok=0;
    for(rint i=1;i<=5;i++)
        for(rint j=1;j<=7;j++)
        {
            if(i>=2&&i<=4&&map[i][j]==map[i-1][j]&&map[i][j]==map[i+1][j]&&map[i][j])
                no[i-1][j]=no[i+1][j]=no[i][j]=ok=1;
            if(j>=2&&j<=6&&map[i][j]==map[i][j-1]&&map[i][j]==map[i][j+1]&&map[i][j])
                no[i][j-1]=no[i][j+1]=no[i][j]=ok=1;
        }
    if(!ok)return 0;
    for(rint i=1;i<=5;i++)
        for(rint j=1;j<=7;j++)
            if(no[i][j])no[i][j]=map[i][j]=0;
    return 1;                
}
bool finish()
{
    for(rint i=1;i<=5;i++)
        if(map[i][1])return 0;
    return 1;
}
void move(int i,int j,int x)
{
    swap(map[i][j],map[i+x][j]);
    drop();
    while(boom())drop();
}
void dfs(int x)
{
    if(finish())
    {
        for(rint i=1;i<=n;i++)
        {
            for(rint j=1;j<=3;j++)printf("%d ",ans[i][j]);
            puts(" ");
        }
        exit(0);
    }
    if(x>n)return ;
    save(x);
    for(rint i=1;i<=5;i++)
        for(rint j=1;j<=7;j++)
        {
            if(map[i][j])
            {
                if(i<=4&&map[i][j]!=map[i+1][j])
                {
                    move(i,j,1);
                    ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=1;
                    dfs(x+1);
                    recover(x);
                    ans[x][1]=ans[x][2]=ans[x][3]=-1;
                }
                if(i>=2&&!map[i-1][j])
                {
                    move(i,j,-1);
                    ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=-1;
                    dfs(x+1);
                    recover(x);
                    ans[x][1]=ans[x][2]=ans[x][3]=-1;
                }
            }
        }
}

int main()
{
    n=Read();
    for(rint i=1;i<=5;i++)
        for(rint j=1;j<=8;j++)
        {
            int x;
            x=Read();
            if(!x)break;
            map[i][j]=x;
        }
    memset(ans,0xFF,sizeof(ans));dfs(1);
    puts("-1");
    return 0;
}
View Code

 

posted @ 2019-06-12 13:26  Rorschach_XR  阅读(158)  评论(0编辑  收藏  举报
//雪花飘落效果