hdu 5899 oasis in desert(acm/icpc 沈阳赛区网络赛,Floyd+二分图判定+最大匹配,好难啊)

来源:互联网 发布:全国行政区街道数据库 编辑:程序博客网 时间:2024/05/22 11:33

传送门: http://acm.hdu.edu.cn/showproblem.php?pid=5899

题目大意:

沙漠中有N个绿洲,由M条路相连,每条路的长度为L。

定义两个集合:
(1)最大危险集:对于集合中任意两点,其最小距离大于k,满足此条件的集合有多个,取最大的那个。
(2)最小安全集:对于图中任意两点,只要他俩的距离≤k,那么最小安全集中至少包含这两个点中的一个。满足条件的集合有多个,取最小的那个。

问这个图中存不存在一个点集,它既是最大危险集,又是最小安全集。
如果存在,输出字典序最小的那个集合,如果不存在,输出Impossible。

题目分析:
其实拿到这个题,想到了跟二分图匹配有关系,但一开始是把原图的点拆成两个,怎么想也对不上。看了这位大神http://blog.csdn.net/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 0x3f3f3f3fint 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();    }}
0 0