很水很水的一道搜索题
分别用dfs和bfs写
bfs
其实更容易理解一点
push旧节点拓展出来的新节点,pop旧节点,直到队列为空。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =110;
int a[N][N];
bool m[N][N];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
queue<pair<int, int>> q;
pair<int,int>p;
void bfs(int x,int y)
{
m[x][y]=1;
p.first = x;
p.second = y;
q.push(p);
while(!q.empty())
{
int nx = p.first;
int ny = p.second;
q.pop();
for(int i=0;i<=3;i++)
{
if(!a[dx[i]+nx][dy[i]+ny]&&!m[dx[i]+nx][dy[i]+ny])
{
p.first = dx[i] + nx;
p.second = dy[i] + ny;
m[p.first][p.second]=1;
q.push(p);
}
}
}
}
int main()
{
int n;
cin>>n;
for(int i=0;i<=n+1;i++)
{
for(int j=0;j<=n+1;j++)
{
a[i][j]=1;
m[i][j]=1;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
if(a[i][j]==1)
m[i][j]=1;
else m[i][j]=0;
//cout<<m[i][j]<<' ';
}
}
int nx,ny;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((i==1||j==1||i==n||j==n)&&a[i][j]==0)
{
//cout<<i<<' '<<j<<endl;
bfs(i,j);
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
//cout<<m[i][j]<<' ';
if(m[i][j]==0)
cout<<2<<' ';
else cout<<a[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =110;
int a[N][N];
bool m[N][N];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
void dfs(int x,int y)
{
if(m[x][y]==1)
return;
m[x][y]=1;
for(int i=0;i<=3;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
if(a[nx][ny]==0&&m[nx][ny]==0)
dfs(nx,ny);
}
}
int main()
{
int n;
cin>>n;
for(int i=0;i<=n+1;i++)
{
for(int j=0;j<=n+1;j++)
{
a[i][j]=1;
m[i][j]=1;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
if(a[i][j]==1)
m[i][j]=1;
else m[i][j]=0;
//cout<<m[i][j]<<' ';
}
}
int nx,ny;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((i==1||j==1||i==n||j==n)&&a[i][j]==0)
{
//cout<<i<<' '<<j<<endl;
dfs(i,j);
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
//cout<<m[i][j]<<' ';
if(m[i][j]==0)
cout<<2<<' ';
else cout<<a[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
砝码问题:
两个剪枝
1.从质量大的砝码开始遍历,避免了很多不必要的递归。
2.维护一个前缀和,当当前选的砝码之前的砝码和小于容量,直接全拿,跳出递归。
P5194 [USACO05DEC]Scales S
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll maxn=0;
ll a[1010];
ll sum[1010];
ll c;
bool t[1010];
void dfs(ll w,int p)
{
if(w>c||p==0)
return;
maxn=max(maxn,w);
if(sum[p-1]+w<c)
{
maxn=max(maxn,sum[p-1]+w);
return ;
}
for(int i=1;i<p;i++)
{
dfs(w+a[i],i);
}
}
int main()
{
ll n;
cin>>n>>c;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum[i]+=sum[i-1]+a[i];
}
dfs(0,n+1);
cout<<maxn<<endl;
}
刺杀大使
洛谷链接
二分加dfs
枚举所有路径,记录伤害值,如果超过mid就往大搜,反之往小搜。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3+10;
int mp[N][N];
bool vis[N][N];
int n,m;
int f;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
void dfs(int xx,int yy,int mid)
{
if(xx==n)
{
f=1;
return;
}
for(int i=0;i<=3;i++)
{
int x=xx+dx[i];int y=yy+dy[i];
if(x<1||x>n||y<1||y>m||vis[x][y]||mp[x][y]>mid)
continue;
vis[xx][yy]=1;
dfs(x,y,mid);
if(f)
return;
}
}
int main()
{
cin>>n>>m;
int maxn=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>mp[i][j];
maxn=max(maxn,mp[i][j]);
}
}
int l=0,r=maxn;
while(l<r)
{
int mid=(l+r)/2;
memset(vis,0,sizeof(vis));
f=0;
dfs(1,1,mid);
if(f)
r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
油滴拓展
N最大为6,一眼全排列,有很多小细节需要注意:
1.当前计算当前油滴的半径时,要查一下是否已经落在之前拓展过的油滴内部,如果有的话,按照题意,应该直接跳过。
2.遍历油滴时,一定只遍历已经滴过的油滴。
#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define gcd(i, j) __gcd(i, j)
using namespace std;
const double pi=3.141592653589793238462643383;
struct node
{
double nx,ny;
double r;
int num;
}a[10];
int b[15];
double dis(double x,double y,double xx,double yy)
{
return(sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy)));
}
int cmp(node x,node y)
{
return x.num<y.num;
}
double rd(double x)
{
if(x-(int) x<0.50)
return floor(x);
else return ceil(x);
}
stack<int>st;
map<string,int>mp;
const int N = 1100;
int main()
{
int n;
cin>>n;
int x,y,xx,yy;
cin>>x>>y>>xx>>yy;
for(int i=1;i<=n;i++)
{
cin>>a[i].nx>>a[i].ny;
a[i].num=i;
}
double sumxy=fabs(x-xx)*fabs(y-yy);
//cout<<sumxy<<endl;
double sum=0;
double ans=1e9;
do
{
sum=0;
for(int i=1;i<=n;i++)
{
a[i].r=0;
b[i]=a[i].num;
}
for(int i=1;i<=n;i++)
{
double min1=1e9+10;
double min2=1e9+10;
min1=min(min(fabs(a[b[i]].nx-x),fabs(a[b[i]].nx-xx)),min(fabs(a[b[i]].ny-y),fabs(a[b[i]].ny-yy)));
for(int j=i-1;j>=1;j--)
{
if(b[i]!=b[j])
{
min2=min(min2,dis(a[b[i]].nx,a[b[i]].ny,a[b[j]].nx,a[b[j]].ny)-a[b[j]].r);
}
}
if(min2<0)
continue;
a[b[i]].r=min(min1,min2);
sum+=a[b[i]].r*a[b[i]].r*pi;
}
//cout<<sum<<endl;
ans=min(ans,round(sumxy-sum));
}
while(next_permutation(a+1,a+n+1,cmp));
printf("%0.lf",rd(ans));
}
图的dfs遍历和bfs遍历
查找文献
用邻接表存储,可以开vector数组,每个m[i],代表与之相连的其他点。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> m[N];
bool vis[N];
queue<int> q;
void dfs(int now)
{
vis[now] = 1;
cout << now << ' ';
for (auto i : m[now])
if (!vis[i])
dfs(i);
}
void bfs(int now)
{
vis[now] = 1;
q.push(now);
while (!q.empty())
{
int temp = q.front();
cout << temp << ' ';
q.pop();
for (auto i : m[temp])
{
if (!vis[i])
{
vis[i] = 1;
q.push(i);
}
}
}
}
int main()
{
int x, y;
cin >> x >> y;
for (int i = 1; i <= y; i++)
{
int u, v;
cin >> u >> v;
m[u].push_back(v);
}
for (int i = 1; i <= x; i++)
sort(m[i].begin(), m[i].end());
dfs(1);
cout << endl;
memset(vis, 0, sizeof(vis));
bfs(1);
}
icpc济南站k题
dfs遍历树,将经过第i个节点时走过的步数累加起来,并将回溯时经过的步数加上,除以n-1就是所求期望。
思路其实是猜的,虽然样例过了,但是在比赛的时候队友认为应该记录先走最长的那个路径,当时我也觉得很有道理,但是比赛前几天刚写过一个遍历邻接表的题,就随手写出来随机路线遍历的写法,没想到直接a掉了。
赛后仔细考虑发现走那条路其实都一样,这里插入lyf的博客:
凡佬博客
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
vector<int>a[N];
int vis[N];
int sum=0;
int ans=0;
void dfs(int now)
{
sum++;
ans+=sum-1;
//cout<<now<<' ';
vis[now]=1;
for(auto i:a[now])
{
if(!vis[i])
{
dfs(i);
sum++;
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
sum=0;ans=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=120;i++)
a[i].clear();
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
a[x].push_back(y);
a[y].push_back(x);
}
dfs(1);
printf("%.6lf",ans*1.0/(n-1))
}
}