HDU6165 FFF at Valentine(并查集+tarjan缩点+拓扑排序)

来源:互联网 发布:金元证券软件下载 编辑:程序博客网 时间:2024/06/09 22:58

题目:这里写图片描述 <—传送门

思路:刚开始用bfs,dfs暴搜T了n发,然后换了个思路用拓扑排序做然后又wa了n发。。。看了官方题解才想到思路有漏洞,顺便学了新姿势缩点。主要是判断两个点之间是否有路径,如果拓扑排序当前层存在两个及以上入度为0的点,那么这些点一定不存在通路。直接拓扑的话会碰到强联通子图,所以要把强联通子图都缩成一个点,然后新建一张图,跑一遍拓扑排序判断一下,就能得出答案。多加了一个并查集判断给的图是不是连通图,不是的话直接Light my fire!,就不用做后面的了。

下面是代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<queue>#include<set>#include<stack> #include<vector>#include<algorithm>#define N 1010#define INF 0x3f3f3f3f#define LL long long#define EPS 1e-8using namespace std;struct point{    int num,k;    friend bool operator< (point a,point b)    {        return a.num<b.num;    }}p[N];vector<int> g[N];   //旧图set<int> g_new[N];  //新图int n,m,xb[N];int pa[N];int dfn[N],low[N],tot,cnt;bool vis[N];stack<int> s;void tarjan(int u){    dfn[u]=low[u]=++tot;    s.push(u);    vis[u]=true;    for(int i=0;i<g[u].size();i++)    {        int &tmp=g[u][i];        if(!dfn[tmp])        {            tarjan(tmp);            low[u]=min(low[u],low[tmp]);        }        else if(vis[tmp])        {            low[u]=min(low[u],dfn[tmp]);        }    }    if(low[u]==dfn[u])  //缩点    {        cnt++;        int tmp;        do{            tmp=s.top();            vis[tmp]=false;            s.pop();            pa[tmp]=cnt;        }while(tmp!=u);    }} void init()   //初始化{        for(int i=1;i<=n;i++)        {            pa[i]=i;        p[i].num=0,p[i].k=i;    }       cnt=tot=0;    memset(vis,false,sizeof(vis));    memset(dfn,0,sizeof(dfn));    memset(low,0,sizeof(low));  }    void clear_g()  //删除建的边{    for(int i=1;i<=n;i++)        g[i].clear();    for(int i=1;i<=cnt;i++)        g_new[i].clear();}int findset(int v)    {        int t1,t2=v;        while(v!=pa[v])            v=pa[v];        while(t2!=pa[t2])              {            t1=pa[t2];            pa[t2]=v;            t2=t1;        }        return v;    }   void union_nodes(int a, int b)    {        int a1=findset(a);        int b1=findset(b);      if(a1!=b1)         {          pa[a1]=b1;          }  }   bool sol()      //拓扑排序得出答案{    int k=1;    while(k<=cnt)    {        sort(p+k,p+cnt+1);         //cout<<p[k].k<<endl;        if(p[k].num>0) return true;        if(k<cnt&&p[k].num==p[k+1].num) return false;        for(int i=k+1;i<=cnt;i++)            xb[p[i].k]=i;        p[k].num=-1;        for(set<int>::iterator it=g_new[p[k].k].begin();it!=g_new[p[k].k].end();it++)        {            p[xb[*it]].num--;        }        k++;    }    return true;}int main(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        init();         int u,v;        for(int i=0;i<m;i++)        {            scanf("%d%d",&u,&v);            union_nodes(u,v);            g[u].push_back(v);        }        bool flag=true;        for(int i=1;i<=n;i++) //并查集判断是否为连通图        {            if(findset(i)!=findset(1))            {                flag=false;                break;            }        }        if(!flag)         {            printf("Light my fire!\n");            clear_g();            continue;        }        for(int i=1;i<=n;i++)        {            if(!dfn[i])                tarjan(i);        }        //cout<<cnt<<endl;        for(int i=1;i<=n;i++)  //建新图        {            //cout<<pa[i]<<endl;            for(int j=0;j<g[i].size();j++)            {                int &tmp=g[i][j];                if(pa[tmp]!=pa[i])                    g_new[pa[i]].insert(pa[tmp]);            }        }        for(int i=1;i<=cnt;i++)  //计算入度        {            //cout<<i<<' '<<g_new[i].size()<<endl;            for(set<int>::iterator it=g_new[i].begin();it!=g_new[i].end();it++)            {                p[*it].num++;                //cout<<*it<<endl;            }        }        /*for(int i=1;i<=cnt;i++)            cout<<p[i].k<<' '<<p[i].num<<endl;*/        if(sol()) printf("I love you my love and our love save us!\n");        else printf("Light my fire!\n");        clear_g();    }}
阅读全文
0 0
原创粉丝点击