题意是有n个点,和另外m个点,T次询问,在n个点里找到最小个数的点去包围住这m个点
n和m给的数据都比较小,所以,n3也可以过,本来很明显是一个凸包,但是,要围住的是给定的m个点,所以需要先判断边的合法性,具体来说,对于n个点两两组成的边,所有的m个点都必须在该边的同侧,进一步说是内侧,所以选择有向存图,按照输入的点的顺序,确定有向边,规定所有的点都必须在该边的左侧,不然就不合法,该边的边权是无穷,合法即为1.
处理完这一步,接下来就是找最小环的过程,这里可以直接使用弗洛伊德算法,o(n3)的时间复杂度,与最短路不同的是,邻接表中e[i][i]初始化是无穷,并且要遍历,表示环。
标准的弗洛伊德算法如下:
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}
}
}
下面是整个代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int e[1000][1000];
struct stu{
int x,y;
}a[1000],b[1000];
int check(stu a,stu b,stu c){//点在左右侧的判断
return (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x);
}
signed main(){
int inf=1000005;
int t;cin>>t;
while(t--){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
}
for(int i=1;i<=m;i++){
cin>>b[i].x>>b[i].y;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
e[i][j]=inf;
if(i!=j){
e[i][j]=1;
for(int k=1;k<=m;k++){
if(check(a[i],a[j],b[k])<=0){
e[i][j]=inf;
}
}
}
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}
}
}
int ans=inf;
for(int i=1;i<=n;i++){
ans=min(e[i][i],ans);
}
if(ans!=inf){
cout<<ans<<endl;
}
else{
cout<<-1<<endl;
}
}
}