搜索专题 题解
一开始写在了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; }
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; }
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; }
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; }
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; }
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; }