大法师(简单题总结)
第一题:吃奶酪
它也有两个小剪枝
1.就是当搜到一个数,但是它已经超过了已知答案,就return。
2.就是提前把距离都预处理出来
Code:
#include<bits/stdc++.h>
using namespace std;
int n;
bool vis[1005];
double ans=10000000,x[1005],y[1005],len[1010][1010];
void dfs(int step,double l,int now){
if(l>ans) return;//剪枝1
if(step==n){
if(ans>l) ans=l;
return;
}
for(int i=1;i<=n;i++){
if(!vis[i]) continue;
vis[i]=0;
dfs(step+1,l+len[now][i],i);
vis[i]=1;
}
}
int main(){
scanf("%d",&n);
x[0]=0;y[0]=0;
for(int i=1;i<=n;i++){
scanf("%lf%lf",&x[i],&y[i]);
vis[i]=1;
}
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
len[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));//剪枝2
}
}
dfs(0,0.0,0);
printf("%.2lf",ans);
}
第二题:健康的荷斯坦奶牛
这题有点复杂(至少我这么觉得)
虽然这是pj-,但我觉得这是tg-
我看这道题,想了半天递归的出口到底是什么。
然后我想就稍稍看了一眼题解 ,哦原来这道题要这么想!!好神奇!!
看代码吧,这道题我没想出来。(回头要再看看这道题)
Code:
#include<bits/stdc++.h>
using namespace std;
int G,V,v[3000],g[3000][3000],c[3000],wer[3000],answer=1000000;
bool check(int x){
for(int i=1;i<=V;i++){
int ans=0;
for(int j=1;j<=x;j++)
ans+=g[c[j]][i]; //非常神奇
if(ans<v[i]) return false;
}
return true;
}//这个check()函数是我第一次遇见。
void dfs(int t,int s){//t是第几种,s是一共用了几种
if(t>G){//结束的地方就是当要用的种类已经超过了所有的种类
if(check(s)){//check()函数,看看这个满足条件吗
if(s<answer){
answer=s;
for(int i=1;i<=answer;i++){
wer[i]=c[i];
}
}
}
return;
}
//这个大法师是我觉得最好的地方
c[s+1]=t;//如果选t
dfs(t+1,s+1);//大法师
c[s+1]=0;//回溯
dfs(t+1,s);//大法师(没选t)
}
int main(){
scanf("%d",&V);
for(int i=1;i<=V;i++) scanf("%d",&v[i]);
scanf("%d",&G);
for(int i=1;i<=G;i++){
for(int j=1;j<=V;j++){
scanf("%d",&g[i][j]);
}
}
dfs(1,0);
printf("%d",answer);
for(int i=1;i<=answer;i++) printf(" %d",wer[i]);
return 0;
}
第三题:取数游戏
这道题的难点在于可能多个点把一个点设为不可选,所以当回溯时只可以--,
而不能让它直接=0。
#include<bits/stdc++.h>
using namespace std;
int a[100][100],step1,use[100][100],step2,ans,n,m,T;
int a1[8]={1,1,1,0,0,-1,-1,-1},a2[8]={0,1,-1,1,-1,0,1,-1};
void dfs(int step1,int step2,int sum){
if(step2>m){
step1++;
step2=1;
}
if(step1>n){
if(ans<sum) ans=sum;
return;
}
if(use[step1][step2]==0){
//就是下面这句有可能有好几个数使得这个地方不可选
for(int i=0;i<=7;i++) use[step1+a1[i]][step2+a2[i]]++;
dfs(step1,step2+2,sum+a[step1][step2]);
for(int i=0;i<=7;i++) use[step1+a1[i]][step2+a2[i]]--;
//上面这句,只能去-- (要不然就爆炸)
}
dfs(step1,step2+1,sum);
}
int main(){
scanf("%d",&T);
while(T--){
ans=0;
memset(use,0,sizeof(use));
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
dfs(1,1,0);
printf("%d\n",ans);
}
return 0;
}
第四题:油滴扩展
这道题的半径是难点,关键在于如何确定出来半径的大小。
#include<bits/stdc++.h>
using namespace std;
int n;
double area,ans,d1,d2,d3,d4,xx1,yy1,xx2,yy2;
double c[1000],x[1000],y[1000],use[1000],len[105][105];
void dfs(int shot,double sum){
if(shot>n){
if(sum>ans) ans=sum;
return;
}
for(int k=1;k<=n;k++){
if(use[k]==0){
c[k]=min(
min(abs(x[k]-d1),abs(x[k]-d2))
,
min(abs(y[k]-d3),abs(y[k]-d4))
);
for(int j=1;j<=n;j++)
if(use[j]) c[k]=min(c[k],len[k][j]-c[j]);
if(c[k]<0) c[k]=0;
use[k]=1;
dfs(shot+1,sum+(M_PI*c[k]*c[k]));
use[k]=0;
}
}
}
int main(){
scanf("%d%lf%lf%lf%lf",&n,&xx1,&yy1,&xx2,&yy2);
for(int i=1;i<=n;i++){
scanf("%lf%lf",&x[i],&y[i]);
x[i]+=1000;
y[i]+=1000;
}
xx1+=1000,xx2+=1000,yy1+=1000,yy2+=1000;
d1=min(xx1,xx2);
d2=max(xx1,xx2);
d3=min(yy1,yy2);
d4=max(yy1,yy2);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
len[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
area=(abs(xx1-xx2)*abs(yy1-yy2));
dfs(1,0.0);
printf("%d",int(area-ans+0.5));
return 0;
}
第五题:小埋与扫雷
这道题给的链接,一共有两个!!!
都十分的重要,而我却一个都没看,所以💩
尤其是八连通,我以为是大于等于8的连通块,实际是8个方向💩
好吧,但是八连通怎么求还是值得看一眼的,看代码(不要以为我大法师,百凤山不分),我只是懒的写了。
#include<bits/stdc++.h>
using namespace std;
int ans,n,m,a[1005][1005],a1[8]={1,1,1,0,0,-1,-1,-1},a2[8]={1,0,-1,1,-1,1,0,-1};
void bfs(int p,int q){
if(p>=0&&p<=n&&q>=0&&q<=m&&a[p][q]==-1){
a[p][q]=2;
for(int i=0;i<=7;i++) bfs(p+a1[i],q+a2[i]);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<=n+1;i++)
for(int j=0;j<=m+1;j++)
a[i][j]=2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1) continue;
int jb=0;
for(int k=0;k<=7;k++) if(a[i+a1[k]][j+a2[k]]==1) jb=1;
if(jb==1) a[i][j]=2;
if(jb==0) a[i][j]=-1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1||a[i][j]==-1) continue;
int jb=0;
for(int k=0;k<=7;k++) if(a[i+a1[k]][j+a2[k]]==0||a[i+a1[k]][j+a2[k]]==-1) jb=1;
if(jb==0) ans++,a[i][j]=3;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]==-1) ans++,bfs(i,j);
cout<<ans;
return 0;
}
第六题:海战
最开始看这道题的时候我还想,这不就是上面的题的求连通块放下来吗?
于是,真香
它需要判断连通块是否是一个矩形,其实这也十分简单,因为根据第一篇题解
不是矩形一定会这样的景象:
额,我废了好大的劲,可是为啥还是这么丑!!!
所以得出:如果4个格里有仨是有数的则不是矩形!
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1005][1005],ans,rr,yy,ss;
char ch;
void dfs(int p,int q){
if(p>=0&&q>=0&&p<=n&&q<=m&&a[p][q]==1){
a[p][q]=2;
dfs(p+1,q);
dfs(p-1,q);
dfs(p,q+1);
dfs(p,q-1);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>ch;//这里有个天大的疑问,为啥我打scanf就会崩?!
if(ch=='.') a[i][j]=0;
else a[i][j]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1){
ans++;
dfs(i,j);
}
}
}
//从这开始判断是不是矩形
for(int i=1;i<=n-1;i++){
for(int j=1;j<=m-1;j++){
int sum=a[i][j]+a[i+1][j]+a[i+1][j+1]+a[i][j+1];
if(sum==6){
cout<<"Bad placement.";
return 0;
}
}
}
// 到这结束
printf("There are %d ships.",ans);
return 0;
}
第七题:‘SEARCH'
额, 我不会搜索做,准确来说是不会剪枝,于是我一生气,就用模拟把它做出来了,可是我还是不会用搜索做,所以再等等,我问问大佬,应该就能出来。
模拟code 60ms,吸氧之后45ms:
#include<bits/stdc++.h>
using namespace std;
int n,m,r,a[55][55],cnt=1,way[1005],x,y;
char ch,c[5],ac[55][55];;
void wes(int p,int q,int temp){
for(int i=q-1;i>=1;i--){
if(a[p][i]==-1) break;
a[p][i]=temp;
}
}
void eas(int p,int q,int temp){
for(int i=q+1;i<=m;i++){
if(a[p][i]==-1) break;
a[p][i]=temp;
}
}
void sou(int p,int q,int temp){
for(int i=p+1;i<=n;i++){
if(a[i][q]==-1) break;
a[i][q]=temp;
}
}
void nor(int p,int q,int temp){
for(int i=p-1;i>=1;i--){
if(a[i][q]==-1) break;
a[i][q]=temp;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>ch;
if(ch=='*') x=i,y=j,a[i][j]=1;
if(ch=='X') a[i][j]=-1;
}
}
scanf("%d",&r);
for(int i=1;i<=r;i++){
cin>>c;
if(c[0]=='N') way[++cnt]=4;
if(c[0]=='S') way[++cnt]=1;
if(c[0]=='E') way[++cnt]=2;
if(c[0]=='W') way[++cnt]=3;
}
for(int k=2;k<=r+1;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==k-1){
if(way[k]==1) sou(i,j,k);
if(way[k]==2) eas(i,j,k);
if(way[k]==3) wes(i,j,k);
if(way[k]==4) nor(i,j,k);
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==-1) ac[i][j]='X';
if(a[i][j]<=r+1&&a[i][j]!=-1) ac[i][j]='.';
if(a[i][j]==r+1) ac[i][j]='*';
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<ac[i][j];
}
cout<<endl;
}
return 0;
}
TLE七个点(不会剪枝😭)code(吸不吸氧一样):
#include<bits/stdc++.h>
using namespace std;
int n,m,r,a[55][55],cnt,way[1005],x,y;
char ch,c[5],ac[55][55];
void dfs(int p,int q,int temp){
if(way[temp]==0){
return;
}
if(way[temp]==3){
for(int i=q-1;i>=1;i--){
if(a[p][i]==-1) break;
if(a[p][i]<temp) a[p][i]=temp;
dfs(p,i,temp+1);
}
}
if(way[temp]==2){
for(int i=q+1;i<=m;i++){
if(a[p][i]==-1) break;
if(a[p][i]<temp) a[p][i]=temp;
dfs(p,i,temp+1);
}
}
if(way[temp]==1){
for(int i=p+1;i<=n;i++){
if(a[i][q]==-1) break;
if(a[i][q]<temp) a[i][q]=temp;
dfs(i,q,temp+1);
}
}
if(way[temp]==4){
for(int i=p-1;i>=1;i--){
if(a[i][q]==-1) break;
if(a[i][q]<temp) a[i][q]=temp;
dfs(i,q,temp+1);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>ch;
if(ch=='*') x=i,y=j;
if(ch=='X') a[i][j]=-1;
}
}
scanf("%d",&r);
for(int i=1;i<=r;i++){
cin>>c;
if(c[0]=='N') way[++cnt]=4;
if(c[0]=='S') way[++cnt]=1;
if(c[0]=='E') way[++cnt]=2;
if(c[0]=='W') way[++cnt]=3;
}
dfs(x,y,1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==-1) ac[i][j]='X';
if(a[i][j]<=r&&a[i][j]!=-1) ac[i][j]='.';
if(a[i][j]==r) ac[i][j]='*';
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<ac[i][j];
}
cout<<endl;
}
return 0;
}