Drunk(LightOJ-1003)(拓扑排序)(并查集查环)

本文介绍了如何解决 LightOJ 1003 题目,探讨了使用拓扑排序和并查集两种方法来确定是否能够饮用所有类型的饮料。提供了完整的代码实现,并对比了两种方法的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1003 - Drunk


题意:
  给出t组样例每组有m行。
每行有a,b两种饮品。其中a是b的前置条件。(a-->b)
在给定的m行信息中能否喝到全部的饮料
如果可以喝到全部种类的饮料则输出YES否则输出NO。 题目分析:
在了解到这是一个由拓扑排序解决的问题之后,了解了一下 拓扑排序 。<------参考链接
是如何操作实现有向无环图(DAG)的拓扑排序。
这个题目的饮品都是字母组成所以需要map容器转换为数字,方便排序。


代码如下:

#include <bits/stdc++.h> //万能头文件
using namespace std;
const int N=22222;
vector<int>g[N]; //vector用来存储有向边
int c[N],topo[N],n; //c数组用来标记是否访问(0未访问,-1正在访问,1已访问)
//topo数组用来存储拓扑序
bool dfs(int u)
{
    c[u]=-1;
    int len=g[u].size();
    for(int v=0;v<len;v++)
    {
        int x=g[u][v];
        if(c[x]<0) return false; //存在有向环,失败则退出(存在u-->v和v-->u)
        else if(!c[x]&&!dfs(x)) return false;
    }
    c[u]=1;
    topo[--n]=u;//访问完一个节点后加入当前拓扑序的首部
    return true;
}
bool toposort()
{
    memset(c,0,sizeof(c));
    int x=n;
    for(int u=1;u<=x;u++)
    {
        if(!c[u])
        {
            if(!dfs(u))
                return false;
        }
    }
    return true;
}
int main()
{
    int t,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        char s1[66],s2[66];
        map<string,int> a;//map使用之前要清零
        a.clear();
        int m;
        scanf("%d",&m);
        for(int i=0;i<=2*m;i++) g[i].clear();//循环对每一个清零(2*m)
        int num=1;
        for(int i=0;i<m;i++)
        {
            scanf(" %s %s",s1,s2);
            if(!a[s1])  a[s1]=num++;
            if(!a[s2])  a[s2]=num++;
            g[a[s1]].push_back(a[s2]);
        }
        n=num-1;//统计有多少种饮料
        if(toposort())
            printf("Case %d: Yes\n",cas++);
        else
            printf("Case %d: No\n",cas++);
    }
}
通过这个题第一次了解到拓扑排序。
——————————————————————————————————————————————————
@ 强大的室友博客    <---------

之后发现并查集判断是否有更加方便些,代码也相对较短些。

代码如下:

#include <bits/stdc++.h>
using namespace std;
int p[1111],sum;
int f(int x)//查找祖先
{
    if(p[x]==x) return x;
    return p[x]=f(p[x]);//路径压缩
}
void mix(int a,int b)
{
    int x=f(a);
    int y=f(b);
    if(x!=y)    //判断祖先是否相同,不同则合并
        p[y]=x;
    else        //相同则出现环
        sum++;
}
int main()
{
    int t,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        char s1[66],s2[66];
        map<string,int> a;
        a.clear();
        int m,num=1;
        scanf("%d",&m);
        sum=0;
        for(int i=0;i<1111;i++) p[i]=i;
        for(int i=0;i<m;i++)
        {
            scanf(" %s %s",s1,s2);
            if(!a[s1])  a[s1]=num++;
            if(!a[s2])  a[s2]=num++;
            mix(a[s1],a[s2]);
        }

        if(sum==0)
            printf("Case %d: Yes\n",cas++);
        else
            printf("Case %d: No\n",cas++);
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值