题解:P1677 [USACO18FEB] Hoofball B
题解:P1677 [USACO18FEB] Hoofball B
题目简述
给定 \(n\) 个点到起点的距离,这些点排成一行并向离他最近的点建边。求至少从多少个点开始,可以遍历这张图。
前言
什么是反图:
于有向图 \(G = (V, E)\) ,它的 反图 (transpose graph) 指的是点集不变,每条边反向得到的图,即:若 \(G\) 的反图为 \(G'=(V, E')\) ,则 \(E'=\{(v, u)|(u, v)\in E\}\)。
摘自 OI Wiki
题目思路
这题是用到了正难则反的思想:建反图然后暴力枚举。
为什么反图可以求至少从多少个点开始可以遍历图:
因为如果一个图是连通的,那么从任意一个顶点开始都可以遍历到所有其他顶点。通过构造反图,我们可以更容易地识别原图中的连通分量。如果反图中存在孤立的顶点或不连通的分量,那么原图中就存在一些顶点之间的路径长度较长或者无法直接到达的情况。
所以详细的做法是枚举每一个反图中的点来作为起点,然后依次查看之后的点是否已经访问。如果点没有访问过那么就访问所有这个点可以到达的点并始答案增加。最后输出所有答案中的最小值。
代码
#include<bits/stdc++.h>
namespace fasI{//快速读入。
#define BF_SIZE 100000
bool IOerr=0;
inline char nc(){
static char buf[BF_SIZE],*p1=buf+BF_SIZE,*pend=buf+BF_SIZE;
if(p1==pend){
p1=buf;
pend=buf+fread(buf,1,BF_SIZE,stdin);
if(pend==p1){IOerr=1;return -1;}
}
return *p1++;
}
inline bool bla(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
inline void read(int &x){
register char ch;
while(bla(ch=nc()));
if(IOerr){return;}
for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
}
//快读。
void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
//快写。
#undef BF_SIZE
};
using namespace std;
using namespace fasI;//使用快读的名字空间。
const int N=105;
int a[N],ans=INT_MAX,n;//距离,答案,奶牛数量。
bool vis[N];//是否访问。
vector<int> v[105];//图。
void dfs(int x){//dfs暴力求解。
vis[x]=true;
for(int y:v[x]){
if(vis[y]==true) continue;
dfs(y);
}
}
signed main(){
read(n);
for(int i=1;i<=n;i++) read(a[i]);
//输入。
sort(a+1,a+n+1);
//输入无单调性,需排序。
a[n+1]=INT_MAX; a[0]=INT_MIN;
//防止建图出界。
for(int i=1;i<=n;i++)
if(a[i]-a[i-1]>a[i+1]-a[i]) v[i].push_back(i+1);
else v[i].push_back(i-1);
//向离的近的建反图。
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) vis[j]=false;
//多次要清空。
int sum=0;
for(int j=i-1,k=1;k<=n;j=(j+1)%n,k++)
if(vis[j+1]==false)
sum++,dfs(j+1);
//暴力遍历起点。
ans=min(sum,ans);
//打擂台。
}
write(ans);
//输出。
return 0;
}

浙公网安备 33010602011771号