题解
A - X-Plosives (样题)
题目:有n个化合物,每个化合物由两种元素构成,现在按照输入顺序把所有化合物装车,当车上有k个化合物的时候,如果恰好有k种元素,就会发生危险,所以当工人拿到化合物的时候,确保它装车后不会有危险才会将它装入,问有多少个化合物被拒绝装车。
- 并查集判环
- 把每种元素看成结点,每个化合物看成一条边,连接两种元素,当有环存在时就会发生危险.由于这个的输入有些特别,暂且不按照并查集一般形式分解,放上完整代码。
#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
const int MX = 1e5+10;
int parent[MX];
int findset(int x)
{
return parent[x] == x ? parent[x] = x:findset(parent[x]);
}
int main()
{
int a, b;
int cnt;
while(scanf("%d", &a) != EOF) //此处用while(1)会超时!
{
cnt = 0;
for(int i = 0; i < MX; ++i) parent[i] = i;
while(a != -1)//针对本题的情况做的输入设计,很巧妙
{
scanf("%d", &b);
int roota = findset(a);
int rootb = findset(b);//这里就是merger部分由于输入的要求产生的变动
if(roota != rootb) parent[roota] = rootb;//没存点就存下,有存点就拒绝
else cnt++;
scanf("%d", &a);
}
printf("%d\n", cnt);
}
}
B - 畅通工程
题目:使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
- 并查集
- 同样是并查集,找到自联的点就可以了。
- 查找
int findset(int x)
{
return parent[x] == x ? parent[x] = x:findset(parent[x]);
}
- 合并
void merger(int le, int ri)
{
int l = findset(le);
int r = findset(ri);
parent[l] = r;
}
- 逻辑部分
for(int i=1;i<=n;i++)
if(parent[i]==i)
ans++;
printf("%d\n",--ans);
C - 小希的迷宫
综合了前两道题的思路,既要保证没有环,又要保证可以都联通。主要有个坑就是,当两个输入都是0的时候输出yes.
- 主体部分
int main()
{
int a,b;
while(scanf("%d%d",&a,&b))
{
if(a==0&&b==0)
{
printf("Yes\n");
continue;
}
if(a==-1&&b==-1)break;//文件结束了
for(int i=0; i<MX; i++)//初始化部分
{
parent[i]=i;
vis[i]=0;
}
bl = 1;//初始化
merger(a,b);//第一个读进去的也要归并
vis[a]=1; vis[b]=1;
while(scanf("%d%d",&a,&b)&&(a+b))//读到0就不读了
{
merger(a,b);
vis[a]=1; vis[b]=1;
}
if(bl==0)//集合里面有
{
printf("No\n");
continue;
}
else
{
bl2 = 0;
for(int i=0; i<=MX; i++)//B题的思想,要保证都有联通的路,坑!
{
if(vis[i]&&parent[i]==i)
bl2++;
}
if(bl2==1) printf("Yes\n");//为什么是1不是0,原因在B题里面
else printf("No\n");
}
}
return 0;
}
D - 确定比赛名次 (样题)
题目:有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。
- 拓扑排序
- 代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int MX = 500+10;
int N, M;
vector<int> G[MX];
int indegree[MX];//记录度
priority_queue<int, vector<int>, greater<int> >q2;//从小到大
void init()//初始化
{
for(int i = 1; i <= M; ++i)
{
int node1, node2;
scanf("%d%d", &node1, &node2);
G[node1].push_back(node2);
indegree[node2]++; //node2的入度加一
}
}
int topo_sort()//拓扑排序
{
for(int i = 1; i <= N; ++i)
if(!indegree[i]) q2.push(i);
int cnt = 0; //用于判断有无环
while(!q2.empty())
{
cnt++;
int tmp = q2.top();//读队头元素
q2.pop(); //弹出队头元素
//遍历这个入度为0的节点指向的所有节点
for(int i = 0; i < G[tmp].size(); ++i)
{
int node = G[tmp][i];
indegree[node]--; //将其指向的节点的入度减1
if(!indegree[node])
q2.push(node);//若入度变为0,入队列
}
//当所有节点都经过队列弹出后说明,存在拓扑排序,即存在有向无环图
if(!q2.empty()) cout << tmp << ' ';
else cout << tmp; }
if(cnt < N) return -1;//若成环则为-1
}
int main()
{
while(scanf("%d%d", &N, &M) != EOF)
{
for(int i = 1; i <= N; ++i)
{
G[i].clear();
indegree[i] = 0;
} //初始化
init();
topo_sort();
cout << endl;
}
return 0;
}
E - Legal or Not
与上一题基本差不多,修改一下输出部分就好了。
- 修改后
void topo_sort()//注意这里的int变成了void
{
for(int i=0; i<n; i++)
if(indegree[i] == 0) q.push(i);
int cnt = 0; //记录弹出队列的节点个数
//从队列中弹出节点进行处理
while(!q.empty())
{
int c = q.front(); //读队头元素
q.pop(); //弹出对头元素
cnt++;
//遍历这个入度为0的节点指向的所有节点,
for(int i=0; i<G[c].size(); i++)
{
indegree[G[c][i]]--; //将其指向的节点的入度减1
if( indegree[G[c][i]] == 0) q.push(G[c][i]); //若入度变为0,入队列
}
}
//当所有节点都经过队列弹出后说明,存在拓扑排序,即存在有向无环图
if(cnt == n) puts("YES");
else puts("NO");
}