https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3639
每次询问给一个点集 问这些点集中两两之间的最短路能否覆盖整个图所有点 如果两点之间有好几条最短路就都算上 且最短路的起点终点也算上
先floyd预处理 然后枚举任意两点之间的转折点 如果e[i][j]==e[i][k]+e[k][j]成立 则说明k在i和j的最短路上 这里就是是这个题的重点
因为不超过40个点 直接状压一下即可
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define pb push_back
typedef long long ll;
const int N=0x3f3f3f3f;
const int maxn=50;
vector <int> gou;
ll pre[maxn],mat[maxn][maxn];
int e[maxn][maxn];
int n,q;
void init()
{
int i;
pre[0]=1;
for(i=1;i<=40;i++) pre[i]=2ll*pre[i-1];
}
void floyd()
{
int i,j,k;
for(k=1;k<=n;k++){
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}
}
}
memset(mat,0,sizeof(mat));
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
for(k=1;k<=n;k++){//
if(e[i][j]==e[i][k]+e[k][j]){
mat[i][j]|=pre[k-1];
}
}
}
}
}
int main()
{
ll ans;
int i,j;
char ch;
init();
while(scanf("%d",&n)!=EOF){
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=-N;
}
}
for(i=1;i<=n;i++){
while(1){
scanf("%d",&j);
e[i][j]=1;
ch=getchar();
if(ch=='\n') break;
}
}
floyd();
scanf("%d",&q);
while(q--){
gou.clear();
while(1){
scanf("%d",&j);
gou.pb(j);
ch=getchar();
if(ch=='\n') break;
}
ans=0;
for(i=0;i<gou.size();i++){
for(j=0;j<gou.size();j++){
if(i!=j) ans|=mat[gou[i]][gou[j]];
}
}
if(ans==pre[n]-1) printf("yes\n");
else printf("no\n");
}
}
return 0;
}