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++);
}
}