图的数据结构与基本操作
用vector模拟,基本操作如下:
#include<iostream>
#include<vector>
using namespace std;
const int N=1;
struct node {
int nextnode;//下一个节点编号
int cost;
};
vector<node>Adj[N];//一个Adjvex[i]是一个不定长数组,表示一个node
void graph_clear() {
for(int i=0; i<N; i++)
Adj[i].clear();
}
void init() {//先建图
node t;
for(int i=0; i<N; i++) {
t.nextnode=3;
t.cost=48;
Adj[i].push_back(t);
}
}
void traverse() {//遍历
for(int i=0; i<N; i++) {
for(int j=0; j<Adj[i].size(); j++) { //一个Adj[i]是一个顶点
printf("%d ", Adj[i][j].nextnode);
printf("%d ", Adj[i][j].cost);
}
}
}
int main() {
graph_clear();
init();
traverse();
return 0;
}
case1:Battle Over Cities,懂题意最重要,然后判断联通块,DFS或并查集(注意都需要用vec e[N]存图,然后再father)都可以
// #include<bits/stdc++.h>
// #include<vector>
// using namespace std;
// const int N=1005;
// vector<int>e[N];
// int tree[N];//并查集
// int findRoot(int x) {
// if(tree[x]==-1) return x;//他爸是-1
// else {
// int root=findRoot(tree[x]);
// tree[x]=root;
// return root;
// }
// }
// int n,m,k;//城市,路,query
// /*
// 注意:还是要先存一个edge,然后在相应的query时做联通操作
// */
// int main() {
// cin>>n>>m>>k;
// while(m--) {
// int a,b;
// cin>>a>>b;
// e[a].push_back(b);
// e[b].push_back(a);
// }
// while(k--) {
// int q,ans=-1;//query
// cin>>q;
// memset(tree,-1,sizeof(tree));
// for(int i=1; i<=n; i++) {
// for(int j=0; j<e[i].size(); j++) {
// if(i==q || e[i][j]==q) continue;
// int a=findRoot(i);
// int b=findRoot(e[i][j]);
// if(a!=b) tree[a]=b;
// }
// }
// for(int i=1; i<=n; i++) {
// if(i==q) continue;
// if(tree[i]==-1) ans++;
// }
// printf("%d\n",ans);
// }
// return 0;
// }
//法2:DFS
#include<bits/stdc++.h>
#include<vector>
using namespace std;
const int N=1005;
vector<int>e[N];
int n,m,k;//城市,路,query
int q,ans=-1;//query
int vis[N]= {0};
/*
注意:还是要先存一个edge,然后在相应的query时做联通操作
*/
void DFS(int x) {
for(int j=0; j<e[x].size(); j++) {
int nd=e[x][j];
if(nd!=q && !vis[nd]) {
vis[nd]=1;
DFS(e[x][j]);
}
}
}
int main() {
cin>>n>>m>>k;
while(m--) {
int a,b;
cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
while(k--) {
cin>>q;
ans=-1;
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
if(i!=q && !vis[i]) {
ans++;
DFS(i);
}
printf("%d\n",ans);
}
return 0;
}
case2:Forwards on Weibo,虽然30分,但是感觉超简单,BFS一把过
#include<bits/stdc++.h>
#include<vector>
#include<queue>
using namespace std;
const int N=1004;
vector<int>e[N];
int layer[N]= {0};
int vis[N]= {0};
int n,L,k,ans;//人数,层数限制
void BFS(int x) {
queue<int>q;
q.push(x);
while(!q.empty()) {
int t=q.front();
// cout<<t<<' ';
if(layer[t]>=L) break;
q.pop();
for(int i=0; i<e[t].size(); i++) {
int nd=e[t][i];
if(vis[nd]) continue;
vis[nd]=1;
layer[nd]=layer[t]+1;
q.push(nd);
ans++;
}
}
}
int main() {
cin>>n>>L;
for(int i=1,a,t; i<=n; i++) {
cin>>t;
while(t--) {
cin>>a;
e[a].push_back(i);
}
}
cin>>k;
while(k--) {
int x;
cin>>x;
ans=0;
memset(layer,0,sizeof(layer));
memset(vis,0,sizeof(vis));
vis[x]=1;
BFS(x);
cout<<ans<<endl;
}
return 0;
}
并查集
- 用 tree数组来表示集合,每个元素的值表示其父节点,tree[x]==-1表示x为根
- 路径压缩:每次将元素直接指向该集合的root,而不要通过传递来找到,这样可以显著提高查询效率
- “合并”x和y,也就是将他们放到同一棵树上,编程可以tree[x]=y这样
case1:畅通工程,将问题抽象成并查集----联通的城市可以表示到一个集合里(一棵树),最后遍历所有城市,得到tree[x]==-1的个数n,即有n个集合->n个不连通的城市,so 需要修建的道路数ans=n-1
#include<iostream>
#include<cstring>
using namespace std;
const int N=1005;
int tree[N];//用树表示集合,每个元素的值是它的父节点编号
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);//递归找根
tree[x]=root;//路径压缩
return root;
}
}
int find_root2(int x) {
int root, t=x;
while(tree[x]!=-1)
x=tree[x];
root=x;
x=t;//回去,以路径压缩
while(tree[x]!=-1){
t=tree[x];
tree[x]=root;
x=t;
}
return root;
}
int main() {
int n,m;
while(scanf("%d", &n)!=EOF, n!=0) {
memset(tree,-1,sizeof(tree));
scanf("%d", &m);
while(m--) {
int a,b;
scanf("%d%d", &a,&b);
a=find_root2(a);
b=find_root2(b);
if(a!=b) tree[a]=b;
}
int ans=0;
for(int i=1; i<=n; i++) { //遍历每个城镇,如果是-1,说明是一个区域。注意编号是从1开始的
// cout<<tree[i]<<' ';
if(tree[i]==-1) ans++;
}
printf("%d\n", ans-1);
}
return 0;
}
case2:more is better,实话说我没看懂题目。。看了解析才明白是找一个有最多元素集合的元素数
- 用sum来存储每个集合的元素数,用fill函数init为1,然后每次合并时,sum[b]+=sum[a]即可
- 最后O(n)找max,别稀里糊涂sort还搞TLE了
#include<iostream>
#include<map>//不能直接用sort排序
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e7+5;
int tree[N];//用树表示集合,每个元素的值是它的父节点编号
int sum[N];//存一个集合内的元素数
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);
tree[x]=root;
return root;
}
}
int main() {
int n;
while(scanf("%d", &n)!=EOF) {
memset(tree,-1,sizeof(tree));
fill(sum,sum+N,1);//初始化为 1个人4
while(n--) {
int a,b;
scanf("%d%d", &a,&b);
a=find_root(a);
b=find_root(b);
if(a!=b) {
tree[a]=b;
sum[b]+=sum[a];//b是根,故a的加到 b上
}
}
// sort(sum,sum+N);这个是nlogn。。明明就是找一个max,O(n)就可以啊
// printf("%d\n", sum[N-1]);
int maxx=0;
for(int i=0;i<N;i++){
if(tree[i]==-1 && sum[i]>maxx)
maxx=sum[i];
}
printf("%d\n", maxx);
}
return 0;
}
case3:判断连通图,开始还以为用图,后来发现完全又是并查集,检查最后tree[i]==-1的数量是否是1即可
#include<iostream>
#include<cstring>
using namespace std;
const int N=1005;
int tree[N];//用树表示集合,每个元素的值是它的父节点编号
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);//递归找根
tree[x]=root;//路径压缩
return root;
}
}
int main() { //16:06->16:25
int n,m;//顶点数,边数
while(scanf("%d%d", &n,&m)!=EOF, n!=0) {
memset(tree,-1,sizeof(tree));
while(m--) {
int a,b;
scanf("%d%d", &a,&b);
a=find_root(a);
b=find_root(b);
if(a!=b) tree[a]=b;
}
int ok=1;
for(int i=1; i<=n&&ok>=0; i++)//“与”怎么写成逗号了呢
if(tree[i]==-1) {
ok--;//小于0说明有两个root,说明不连通
}
printf("%s\n",ok>=0?"YES":"NO");
}
return 0;
}
case4:Head of a Gang
是gang的条件:一个cluster的总time要>k且人数>2。选head原则:一个cluster里time最大的
最小生成树(MST)
case1:还是畅通工程,问要使各个村庄联通的最小修路长度
- 利用struct存edge(本质为了存 两node间的cost,真正的edge是通过tree来存的)
- 用kruskal算法,首先对edge的cost排序,遍历每一个"cost"之间的node是否在同一个集合(是否已经连通),如果不是就合并,然后加上两node之间的cost
- 最后要判断全图是否连通了
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1005;
int tree[N];//记录node
struct Edge {
int a,b;
int cost;
} e[N]; //只是用于存储两node间的cost,而不一定真有这条edge!真正的edge本质在tree记录
int cmp(Edge a,Edge b) {
return a.cost<b.cost;
}
int n,m;//道路数,村庄数
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);
tree[x]=root;
return root;
}
}
bool is_connected() { //判断图是否连通
for(int i=1,ok=1; i<=m; i++) {
if(tree[i]==-1) ok--;
if(ok<0) return false;
}
return true;
}
int main() {
while(scanf("%d%d", &n,&m)!=EOF, n!=0) {
memset(tree,-1,sizeof(tree));
for(int i=1; i<=n; i++)
scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].cost);
sort(e+1,e+1+n,cmp);
int ans=0;
for(int i=1; i<=n; i++) {
int a=find_root(e[i].a);
int b=find_root(e[i].b);
if(a!=b) { //判断一个edge两段的node是否已经联通
tree[a]=b;
ans+=e[i].cost;
}
}
if(is_connected()) printf("%d\n", ans);//刚开始这个函数忘记打括号了。。没有运行
else printf("?\n");
}
return 0;
}
case2:Freckles,给出了点的位置坐标,问连通的最短路径
- 需要转换成每个edge(n*(n-1)/2个)然后MST
- 在struct里面定义函数,成员函数?
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=105;
int tree[N];//记录node
struct Edge {
int a,b;
double cost;
} e[N*N/2];
bool cmp(Edge x,Edge y) {
if(x.cost<y.cost && fabs(x.cost-y.cost)>1e-6)
return true;
else return false;
// return x.cost<y.cost;
}
struct Node {
double x,y;
double get_dis(Node A) {
return sqrt((x-A.x)*(x-A.x)+(y-A.y)*(y-A.y));
}
} node[N];
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);
tree[x]=root;
return root;
}
}
int main() { //11:40->12:50
int n;
while(scanf("%d", &n)!=EOF, n!=0) {
memset(tree,-1,sizeof(tree));
for(int i=1; i<=n; i++)
scanf("%lf%lf", &node[i].x,&node[i].y);
int e_n=0;//边数,最后是n*(n-1)/2
for(int i=1; i<=n; i++) {
for(int j=i+1; j<=n; j++) {
e[e_n].a=i;
e[e_n].b=j;
e[e_n++].cost=node[i].get_dis(node[j]);
}
}
double ans=0;
sort(e,e+e_n,cmp);
for(int i=0; i<e_n; i++) { //遍历每个edge,这里e从0开始,因为0/1无所谓
int a=find_root(e[i].a);
int b=find_root(e[i].b);
if(a!=b) {
tree[a]=b;
ans+=e[i].cost;
// printf("%.2lf\n", ans);
}
}
printf("%.2lf\n", ans);
}
return 0;
}
case3:Jungle Roads不难,主要是复习kruskal算法了,值得注意的是
- scanf("%c", &ch); 可以读入换行和空格,若想用scanf,就要在%c前加上前面的\n或者空格!否则会读错的
- 名字是字母,在建并查集的时候,因为题目简单,直接对名字都-'A'就可以建立相应的编号了
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=30;
int tree[N];
struct Edge {
char a,b;//名字就是一个字母,序号用 -'A'表示
int cost;
} e[N*N];
int cmp(Edge x, Edge y) {
return x.cost<y.cost;
}
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);
tree[x]=root;
return root;
}
}
int main() {
int num;//城市数
while(scanf("%d", &num)!=EOF, num!=0) {
memset(tree,-1,sizeof(tree));
int e_n=0, n;//边数,一个node连接的node数
char c1, c2;
while(--num) {
scanf("\n%c%d", &c1,&n);
// cin>>c1>>n;//!错误原因是,scanf是可以吃空格的。。。
int co;//cost
while(n--) {
e[e_n].a=c1;
scanf(" %c%d", &c2,&co);
// cin>>c2>>co;
e[e_n].b=c2;
e[e_n++].cost=co;
}
}
sort(e,e+e_n,cmp);
int ans=0;
for(int i=0; i<e_n; i++) { //遍历每个edge
int a=find_root(e[i].a-'A');
int b=find_root(e[i].b-'A');
if(a!=b) {
tree[a]=b;
ans+=e[i].cost;
}
}
printf("%d\n", ans);
}
return 0;
}
case4:继续畅通工程,这题与case1不同之处在于,给了一些已经修好的路,问现在要全连通最少需要的cost。
- 有个非常巧妙的做法就是在cmp函数,先按是否修好排序,再按cost升序排序!最后还是一样用MST做就完全一样了
- 看到另一做法,错的!他还是完全按照cost排序,只是是在input的时候 对已经修好的路添加到并查集里面,但是sort绝对不能这么写,因为有的有的虽然cost大,但是已经连通了的话,宁可有更小cost的edge也不会再修建了
- 在存储好所有edge后的合并操作,最后的结果一定是在给定数据内都连通。因为这里给了每个点之间的关系,共n*(n-1)/2个,全部连通后,在if(a!=b)那里始终不能通过,也就是说始终a==b;而有时候可能某两点之间压根没路,这样当然是无法连通了
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
int tree[N];
struct Edge {
int a,b;//名字就是一个字母,序号用 -'A'表示
int cost;
int ok;//是否已经建好
} e[N*N];
int cmp(Edge x, Edge y) {
if(x.ok!=y.ok) return x.ok>y.ok;//修建好的优先!这个思路非常巧妙
else return x.cost<y.cost;
}
int find_root(int x) {
if(tree[x]==-1) return x;
else {
int root=find_root(tree[x]);
tree[x]=root;
return root;
}
}
int main() {
int n;//城市数
while(scanf("%d", &n)!=EOF, n!=0) {
memset(tree,-1,sizeof(tree));
int m=n*(n-1)/2;
for(int i=0; i<m; i++) {
scanf("%d%d%d%d", &e[i].a,&e[i].b,&e[i].cost,&e[i].ok);
}
sort(e,e+m,cmp);
int ans=0;
for(int i=0; i<m; i++) { //遍历每个edge
int a=find_root(e[i].a);
int b=find_root(e[i].b);
if(a!=b) {//这就是合并操作,最后的结果一定是在给定数据内都连通。
//因为这里给了每个点之间的关系,而有时候可能某两点之间压根没路,这样当然是无法连通了
tree[a]=b;
if(e[i].ok==0)
ans+=e[i].cost;
}
}
printf("%d\n", ans);
}
return 0;
}
最短路径
case1:最短路。
法1:Floyd算法
- 先遍历每一个i,j之间的中介点,遇到i,j与k可达,且i->j的距离比通过k到要大时,更新
- O(n^3)复杂度,一般只撑到200个node。一般用邻接矩阵存储(名字可以序号化,作为数组下标)
- 注意无向图 input时,a->b, b->a都要赋值
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=205;
const int INF=0x3fffffff;
int mapp[N][N];//x->y的cost
int n,m;//路口数,道路数
void Floyd() {
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) {
if(mapp[i][k]!=INF && mapp[k][j]!=INF &&
mapp[i][j]>mapp[i][k]+mapp[k][j])
mapp[i][j]=mapp[i][k]+mapp[k][j];//否则都保持原样
}
}
int main() {
while(scanf("%d%d", &n,&m)!=EOF) {
if(!n&&!m) break;
fill(mapp[0],mapp[0]+N*N,INF);//除了 到自己,都init为INF
for(int i=1; i<=n; i++) mapp[i][i]=0;
while(m--) {
int a,b,c;
scanf("%d%d%d", &a,&b,&c);
mapp[a][b]=mapp[b][a]=c;
}
Floyd();
printf("%d\n", mapp[1][n]);
}
return 0;
}
法2:Dijkstra算法(单元最短路,一个node到其他all nodes的最短路)。(在牛课上,F法31ms,D法15ms)实现思路如下:
- 邻接链表法存储。用vector结构体数组来存图,即一个node的邻node与cost
- dis数组存开始节点s到各node的路径(算法结束后都是最短路),vis数组标记node是否已经扩展过【很像BFS】
- 大循环n-1次,即需要遍历到n-1个节点(每次只添加一个node,so需要n-1次循环)
- 大循环内,每扩展一个node p,需要扩展p的all 邻接节点t,若t不可达,或者通过p到t可以更短,则更新dis
- 同时选择dis里面最小cost的node作为下一次扩展的node【整个算法很像BFS】
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=205;
const int INF=0x3fffffff;
struct Node {
int next;
int cost;
};
vector<Node>e[N];//邻接链表存图
int n,m;//路口数,道路数
int dis[N];//存:从s到i的最短路
int mark[N];//标记是否已经得到:到i的最短路长度(即 是否已经在S集里)
void Dijkstra() {
fill(dis,dis+N,INF);
memset(mark,0,sizeof(mark));
dis[1]=0;//到自己
mark[1]=1;
int p=1;//指新加的node
for(int i=1; i<n; i++) { //n-1次,确定n-1个点的最短路
for(int j=0; j<e[p].size(); j++) { //遍历新加的点的all邻边
int t=e[p][j].next;
if(mark[t]) continue;
int c=e[p][j].cost;
if(dis[t]==INF || dis[t]>dis[p]+c)//t节点尚不可达,or由p到t更短
dis[t]=dis[p]+c;
}
int mi=INF;
for(int j=1; j<=n; j++) { //选下一个最短路的node
if(mark[j] || dis[j]==INF) continue;
if(dis[j]<mi) {
mi=dis[j];
p=j;
}
}
mark[p]=1;
}
}
int main() {
while(scanf("%d%d", &n,&m)!=EOF) {
if(!n&&!m) break;
for(int i=1; i<=n; i++) e[i].clear();
while(m--) {
int a,b,c;
scanf("%d%d%d", &a,&b,&c);
Node t;
t.next=b;
t.cost=c;
e[a].push_back(t);
t.next=a;//无向图,两个方向都要添加
e[b].push_back(t);
}
Dijkstra();
printf("%d\n", dis[n]);
}
return 0;
}
case2:路径+花费的“优先最短路”,与之前不同的地方在于,路径一样的还要选花费少的,所以只要把“更新”和“选择”步骤里判断的条件加上路径相同时,cost少的优先就行啦!还有一点思考的就是,在“选择”里面,本质就是像BFS里面选frontier:已经扩展的node不选,完全没探索的节点不选。不过王道给出了更好写法:
- if(dis[t]==INF || dis[t]>dis[p]+c || dis[t]==dis[p]+d && cost[t]>cost[p]+c) {dis[t]=dis[p]+d;cost[t]=cost[p]+c;} 合并一起写也很清晰
- 另外,“选择下一个扩展结点”的时候,不需要考虑cost!?
- 若最后vis[end_node]==0,说明不存在通路
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int N=105;
vector<int>e[N];//邻接链表
queue<int>q;//保存入度为0的node
int inDegree[N];//统计每个node的入度
int main() {
int n,m;//人数,关系数
while(scanf("%d%d", &n,&m)!=EOF, n!=0) {
for(int i=0;i<N;i++) e[i].clear();
memset(inDegree,0,sizeof(inDegree));
while(m--){
int a,b;
scanf("%d%d", &a,&b);
inDegree[b]++;//入度++
e[a].push_back(b);
}
while(!q.empty()) q.pop();//queue没有clear
for(int i=0;i<n;i++){
if(inDegree[i]==0) q.push(i);//入度为0的入队
}
int cnt=0;
while(!q.empty()){
int p=q.front();//入度为0的node p
cnt++;
q.pop();
for(int i=0;i<e[p].size();i++){//遍历p指向的node
if(--inDegree[e[p][i]]==0) q.push(e[p][i]);
}
}
if(cnt==n) printf("YES\n");//all node能被确定拓扑排序,说明无环
else printf("NO\n");
}
return 0;
}
case3:
DAG的拓扑排序
若a->b则,a必须在b前面,求:满足这样要求的结点序列的过程,称拓扑排序,一个应用如下:
case1:Legal or Not,就是给出了谁是谁master的关系,有传递性,但不能A是B的同时B是A的,本质就是抽象为图的话,不能有环,于是可以用拓扑排序做,如果最终排序的结点数==总人数,说明排序完毕,否则说明有剩余 也就是说明有环。而拓扑排序的编程实现如下:
- 利用一个queue保存all入度为0的node,vector e模拟邻接链表存图,inDegree存node的入度数
- input时存图,之后把入度为0的都入队q,然后从q选一个p出队(从图中删去),并将p指向的node的入度都-1,若其入度也=0了,就入q,直到q为空。若all node都被删去(都出队了一次)说明拓扑排序完成,若没有,则说明有入度!=0的node,也就是环(因为有环的话,环内node的入度都不会到0),拓扑排序失败
- 本题一次出队就cnt++,最后cnt==n就说明legal,也就是无环 成功,否则失败
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int N=105;
vector<int>e[N];//邻接链表
queue<int>q;//保存入度为0的node
int inDegree[N];//统计每个node的入度
int main() {
int n,m;//人数,关系数
while(scanf("%d%d", &n,&m)!=EOF, n!=0) {
for(int i=0;i<N;i++) e[i].clear();
memset(inDegree,0,sizeof(inDegree));
while(m--){
int a,b;
scanf("%d%d", &a,&b);
inDegree[b]++;//入度++
e[a].push_back(b);
}
while(!q.empty()) q.pop();//queue没有clear
for(int i=0;i<n;i++){
if(inDegree[i]==0) q.push(i);//入度为0的入队
}
int cnt=0;
while(!q.empty()){
int p=q.front();//入度为0的node p
cnt++;
q.pop();
for(int i=0;i<e[p].size();i++){//遍历p指向的node
if(--inDegree[e[p][i]]==0) q.push(e[p][i]);
}
}
if(cnt==n) printf("YES\n");//all node能被确定拓扑排序,说明无环
else printf("NO\n");
}
return 0;
}
case2:确定比赛名次,
- 改用了优先队列,queue中用q.front()取元素,而priority_queue用q.top()取。学习优先队列的用法
- 主要init问题!这里需要初始化的有4个:保存入度的q队列、存入度数的inDegree数组、邻接链表e数据、ans数组
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int N=505;
vector<int>e[N];//邻接链表
priority_queue<int,vector<int>,greater<int>>q;//保存入度为0的node
int inDegree[N];//统计每个node的入度
int n,m;//队伍数,关系数
vector<int>ans;
int main() {
while(scanf("%d%d", &n,&m)!=EOF) {
if(!n&&!m) break;
memset(inDegree,0,sizeof(inDegree));
for(int i=0;i<N;i++) e[i].clear();
while(m--) {
int a,b;
scanf("%d%d", &a,&b);
inDegree[b]++;
e[a].push_back(b);//要求相同成绩的,编号小的优先
}
while(!q.empty()) q.pop();
ans.clear();
for(int i=1; i<=n; i++) {
if(inDegree[i]==0) q.push(i);
}
while(!q.empty()) {
int p=q.top();
ans.push_back(p);
q.pop();
for(int i=0; i<e[p].size(); i++) {
if(--inDegree[e[p][i]]==0)
q.push(e[p][i]);
}
}
for(int i=0; i<ans.size()-1; i++)
printf("%d ", ans[i]);
printf("%d\n", ans[ans.size()-1]);
}
return 0;
}
case3:产生冠军,好题,不过还是这个样例给的好,给出了1->2,3->4这种情况也是没有产生冠军的(虽然依然能进行拓扑排序,但是对于本题来说是失败的)
- 关键的是,上面那种情况,其实只要判断初始入度为0的node数是否为1即可(这样说明不会出现两个“冠军”没比的情况),接下来就是和case2一样判断排序是否有环即可
- 因为给的string name,涉及map的应用,判空:mp.count(a)==0,清空可以直接clear
- string 的scanf:a.resize(20);//是字符,不是字节
scanf("%s", &a[0]);
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
using namespace std;
const int N=1005;
vector<int>e[N];
int inD[N];
queue<int>q;
map<string,int>mp;//name->id
vector<string>ans;
int main() { //8:52->9:39:37min
int m;//关系数
while(scanf("%d", &m)!=EOF, m!=0) {
int n=0;//人数
memset(inD,0,sizeof(inD));
mp.clear();
for(int i=0; i<N; i++) e[i].clear();
while(m--) {
string a,b;
a.resize(20);//是字符,不是字节
b.resize(20);
scanf("%s%s", &a[0],&b[0]);
if(mp.count(a)==0)
mp[a]=n++;
if(mp.count(b)==0)
mp[b]=n++;
e[mp[a]].push_back(mp[b]);
inD[mp[b]]++;
}//end input
while(!q.empty()) q.pop();
ans.clear();
int ok=1;//用来判断:不会有2个冠军
for(int i=0,c=0; i<n; i++) {
if(inD[i]==0) {
q.push(i);
c++;
}
if(c>1) {
printf("No\n");
ok=0;
break;
}
}
if(!ok) continue;
int cnt=0;
while(!q.empty()) {
int p=q.front();
cnt++;
q.pop();
for(int i=0; i<e[p].size(); i++) {
if(--inD[e[p][i]]==0)
q.push(e[p][i]);
}
// cout<<p<<' ';
}
printf("%s\n", cnt==n?"Yes":"No");
}
return 0;
}