题目1:奶牛选美
这道题其实就是把两个连通块合成一个,可以用dfs、bfs和并查集。因为最近在dfs专题训练,这里我只写了dfs。
首先我们用dfs的方式遍历两个连通块,将两个连通块中点的坐标分别存入两个数组中,将这两个数组中的点一 一匹配,迭代出最小值。
#include<iostream>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
vector<PII> points[2];
const int N=55;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m;
char g[N][N];
void dfs(int x,int y,vector<PII> &q)
{
q.push_back({x,y});
g[x][y]='.';
for(int i=0;i<4;i++)
{
int cx=x+dx[i],cy=y+dy[i];
if(cx<0||cx>=n||cy<0||cy>=m) continue;
if(g[cx][cy]=='X') dfs(cx,cy,q);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%s",g[i]);
int k=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(g[i][j]=='X') dfs(i,j,points[k++]);
}
int res=N*N;
for(int i=0;i<points[0].size();i++)
for(int j=0;j<points[1].size();j++)
{
int x1=points[0][i].x,y1=points[0][i].y;
int x2=points[1][j].x,y2=points[1][j].y;
int t=abs(x1-x2)+abs(y1-y2)-1;
res=min(res,t);
}
printf("%d",res);
}
题目2:大臣的旅费
首先是最暴力的写法。
以每个点为起点,搜索它的最长路径,最后取所有点为起点时最长路径的最大值。
时间复杂度是O()。
#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+10;
int h[N],e[N],ne[N],w[N],idx=0;
int n;
bool st[N];
int sum=0;
void add(int p,int q,int d)
{
e[idx]=q;
w[idx]=d;
ne[idx]=h[p];
h[p]=idx++;
}
int dfs(int u,int res)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(st[j]) continue;
st[j]=1;
sum=max(sum,dfs(j,res+w[i]));
st[j]=0;
}
return res;
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++)
{
int p,q,d;
scanf("%d%d%d",&p,&q,&d);
add(p,q,d);
add(q,p,d);
}
int res=0;
for(int i=1;i<=n;i++)
{
memset(st,0,sizeof(st));
st[i]=1;
dfs(i,0);
}
printf("%d",((10+1)+(10+sum))*sum/2);
}
接下来考虑该如何优化。
这其实涉及到一个知识点——求树的直径。这个问题有固定的步骤:
①选定一个x,求出x到所有点的距离;
②找到与x距离最远的点y,求出y到所有点的距离;
③y到所有点的最长距离即为答案。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int h[N],e[N],ne[N],w[N],idx=0;
int n;
bool st[N];
int dist[N];
void add(int p,int q,int d)
{
e[idx]=q;
w[idx]=d;
ne[idx]=h[p];
h[p]=idx++;
}
void dfs(int u,int di)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]!=-1) continue;
dist[j]=di+w[i];
dfs(j,dist[j]);
}
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++)
{
int p,q,d;
scanf("%d%d%d",&p,&q,&d);
add(p,q,d);
add(q,p,d);
}
memset(dist,-1,sizeof(dist));
dist[1]=0;
dfs(1,0);
int maxpoint=0,maxval=0;
for(int i=1;i<=n;i++)
if(dist[i]>maxval)
{
maxval=dist[i];
maxpoint=i;
}
memset(dist,-1,sizeof(dist));
dist[maxpoint]=0;
dfs(maxpoint,0);
sort(dist+1,dist+1+n);
int res=dist[n];
printf("%lld",(long long)(10+1+10+res)*res/2);
}
题目3:扫雷
这道题的过程是:遍历排雷火箭,如果排雷火箭的范围(遍历-r到r之间在圆内的所有点)波及到了炸弹,则炸弹爆炸,dfs炸弹的范围。
这道题过完所有数据需要手写哈希,这里没有手写,过了8/11个数据。
#include<iostream>
#include<unordered_map>
using namespace std;
const int N=5e4+10,M=1e9+1;
typedef pair<int,int> PII;
typedef long long ll;
#define x first
#define y second
int n,m;
pair<PII,int> jian[N],lei[N];
unordered_map<ll,int> is_lei;
unordered_map<ll,int> num;
int dx[8]={-1,-1,-1,0,0,1,1,1};
int dy[8]={-1,0,1,-1,1,-1,0,1};
int cnt=0;
void dfs(int x,int y,int r)
{
for(int cx=max(0,x-r);cx<=x+r;cx++)
for(int cy=max(0,y-r);cy<=y+r;cy++)
{
if((cx-x)*(cx-x)+(cy-y)*(cy-y)>r*r) continue;
ll t;
t=cx*M+cy;
if(is_lei.count(t))
{
cnt+=num[t];
int tr=is_lei[t];
is_lei.erase(t);
num.erase(t);
dfs(cx,cy,tr);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d%d",&lei[i].x.x,&lei[i].x.y,&lei[i].y);
for(int i=1;i<=n;i++)
{
ll t;
t=lei[i].x.x*M+lei[i].x.y;
if(is_lei.count(t))
{
is_lei[t]=max(is_lei[t],lei[i].y);
num[t]++;
}
else
{
is_lei[t]=lei[i].y;
num[t]=1;
}
}
for(int i=1;i<=m;i++) scanf("%d%d%d",&jian[i].x.x,&jian[i].x.y,&jian[i].y);
for(int i=1;i<=m;i++) dfs(jian[i].x.x,jian[i].x.y,jian[i].y);
printf("%d",cnt);
}