传送门: http://acm.hdu.edu.cn/showproblem.php?pid=5899
题目大意:
沙漠中有N个绿洲,由M条路相连,每条路的长度为L。
定义两个集合:
(1)最大危险集:对于集合中任意两点,其最小距离大于k,满足此条件的集合有多个,取最大的那个。
(2)最小安全集:对于图中任意两点,只要他俩的距离≤k,那么最小安全集中至少包含这两个点中的一个。满足条件的集合有多个,取最小的那个。
问这个图中存不存在一个点集,它既是最大危险集,又是最小安全集。
如果存在,输出字典序最小的那个集合,如果不存在,输出Impossible。
题目分析:
其实拿到这个题,想到了跟二分图匹配有关系,但一开始是把原图的点拆成两个,怎么想也对不上。看了这位大神http://blog.youkuaiyun.com/wmdcstdio/article/details/52576840的题解就明白了。
这道题应该这样构图:
构建图
G′(V,E′)
,其中
E′={(i,j)|dist(i,j)≤k,(i,j)∈E}
.也就是把原图里最短距离≤k的两个边连起来。
接下来是见证奇迹的时刻~~
我们假设一个点v1属于答案集,那么:
1. v1点相邻的点一定不属于答案集;(因为最大危险集定义)
2. 和1中相邻的点一定属于答案集;(因为最小安全集定义)
3. 和2中相邻的点一定不属于答案集;
….
因此得出结论:
1.图
G′
是个二分图.
2.“最大危险集”即为图
G′
的最大独立集(因为要满足任意两点间距离>k,也就是无边);
3.“最小安全集”即为图
G′
的最小支配集(因为这个集合能“支配”所有小于等于L的路径,也就是
G′
的边嘛)
我们都知道,最大独立集+最小支配集=图的总点数,所以说,答案集存在只需最大匹配=n/2即可。
这里面还有一个问题,我也在这里wa了六七发。那就是输入的原图有可能不连通,这种情况要输出impossible。上述博主在这里似乎有点问题,因为如果不连通,就算每个子图都存在这种答案集,如果取了并集也会存在两个点他们是连不通的,而“最小距离大于k”的前提是存在最小距离,他俩都连不通,就没有意义。所以还要加一条判断就是,图G是连通的!!!
求最小距离用了Floyd算法,判定二分就是裸的dfs,然后求最大匹配用了一个匈牙利算法。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int t,n,m,k,a,b,l;
int p[510];//记录每个点的颜色,0没染,1黑色,2白色
int dis[510][510];//因为要算距离,所以用邻接矩阵存图
vector<int> g[510];
int f[510],vis[510];
int cnt;
void floyd() {
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
void initGraph() {
//构造图g,当i-j边距离小于等于k的时候,给i-j加一条边
for(int i=1;i<=n;i++) {
for(int j=i+1;j<=n;j++) {
if(dis[i][j]<=k) {
g[i].push_back(j);
g[j].push_back(i);
}
}
}
}
bool dfs(int u,int col) { //给i点染col色
//cout<<u<<","<<col<<endl;
p[u]=col;
for(int i=0;i<g[u].size();i++) {
int v=g[u][i];
if(p[v]==col)//如果相邻的两个点颜色一样,那就是非法的
return false;
else if(p[v]==0) {
if(col==1) {
if(!dfs(v,2)) return false;
}
else {
if(!dfs(v,1)) return false;
}
}
}
return true;
}
bool paint() {
//v1.clear();v2.clear();
for(int i=1;i<=n;i++) {
if(p[i]==0) {
p[i]=1;
if(!dfs(i,1))
return false;
}
}
return true;
}
bool dfs2(int i) {
vis[i]=1;
for(int j=0;j<g[i].size();j++) {
int u=g[i][j];
if(f[u] == -1 || (!vis[f[u]] && dfs2(f[u]))) {
f[u]=i;
f[i]=u;
return 1;
}
}
return 0;
}
bool match() {
memset(f,-1,sizeof(f));
for(int i=1;i<=n;i++) {
if(f[i]==-1) {
memset(vis,0,sizeof(vis));
cnt+=dfs2(i);
}
}
return cnt*2==n;
}
void solve() {
int con=1;
for(int i=1;i<=n;i++)
if(dis[1][i]<INF)
con++;
if(!paint() || con<n || !match())
printf("Impossible\n");
else {
vector<int> ans;
for(int i=1;i<=n;i++)
if(p[i]==1)
ans.push_back(i);
printf("%d\n", ans.size());
for(int i=0;i<ans.size()-1;i++)
printf("%d ", ans[i]);
printf("%d\n", ans.back());
}
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d %d %d",&n,&m,&k);
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=n;i++)
dis[i][i]=0;
memset(p,0,sizeof(p));
memset(g,0,sizeof(g));
cnt=0;
while(m--) {
scanf("%d %d %d",&a,&b,&l);
dis[a][b]=l;dis[b][a]=l;
}
if(n==0) { //特判0个点
printf("0\n");
continue;
}
floyd();
initGraph();
solve();
}
}