#include
using namespace std;
#define MaxVnum 100 //顶点数最大值
typedef char VexType; //顶点的数据类型,根据需要定义
typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct{
VexType Vex[MaxVnum];
EdgeType Edge[MaxVnum][MaxVnum];
int vexnum,edgenum; //顶点数,边数
}AMGraph;
Vex[]
中。Vex[]
中的存储下标 i i i、 j j j,令 E d g e [ i ] [ j ] = E d g e [ j ] [ i ] = 1 Edge[i][j] = Edge[j][i] = 1 Edge[i][j]=Edge[j][i]=1。Vex[]
中的存储下标 i i i、 j j j,令 E d g e [ i ] [ j ] = 1 Edge[i][j] = 1 Edge[i][j]=1。Vex[]
中的存储下标 i i i、 j j j,令 E d g e [ i ] [ j ] = E d g e [ j ] [ i ] = w Edge[i][j] = Edge[j][i] = w Edge[i][j]=Edge[j][i]=w。Vex[]
中的存储下标 i i i、 j j j,令 E d g e [ i ] [ j ] = w Edge[i][j] = w Edge[i][j]=w。#include
using namespace std;
#define MaxVnum 100 //顶点数最大值
typedef char VexType; //顶点的数据类型,根据需要定义
typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct{
VexType Vex[MaxVnum];
EdgeType Edge[MaxVnum][MaxVnum];
int vexnum,edgenum; //顶点数,边数
}AMGraph;
int locatevex(AMGraph G,VexType x); //查找顶点信息的下标
void CreateAMGraph(AMGraph &G); //构造权值矩阵
void show(AMGraph G); //打印邻接矩阵
int main(){
AMGraph cs;
CreateAMGraph(cs);
show(cs);
}
int locatevex(AMGraph G,VexType x){
for(int i=0;i<G.vexnum;i++)
if(x==G.Vex[i])
return i;
return -1;//没找到
}
void CreateAMGraph(AMGraph &G){
int i,j;
VexType u,v;
cout<<"请输入顶点数:"<<endl;
cin>>G.vexnum;
cout<<"请输入边数:"<<endl;
cin>>G.edgenum;
cout<<"请输入顶点信息:"<<endl;
for(int i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
cin>>G.Vex[i];
for(int i=0;i<G.vexnum;i++)//初始化邻接矩阵所有值为0,如果是网,则初始化邻接矩阵为无穷大
for(int j=0;j<G.vexnum;j++)
G.Edge[i][j]=0;
cout<<"请输入每条边依附的两个顶点:"<<endl;
while(G.edgenum--){
cin>>u>>v;
i=locatevex(G,u);//查找顶点u的存储下标
j=locatevex(G,v);//查找顶点v的存储下标
if(i!=-1&&j!=-1)
G.Edge[i][j]=G.Edge[j][i]=1; //邻接矩阵储置1
else{
cout << "输入顶点信息错!请重新输入!"<<endl;
G.edgenum++;//本次输入不算
}
}
}
void show(AMGraph G){
for (int i = 0; i < G.vexnum; ++i) {
for (int j = 0; j < G.vexnum; ++j) {
cout << G.Edge[i][j] << " ";
}
cout << endl;
}
}
输入:
4
5
a b c d
a b
a d
b c
b d
c d
输出:
0 1 0 1
1 0 1 1
0 1 0 1
1 1 1 0
v
和指向下一个邻接点的指针next
,如果是网的邻接点,则还需增加一个权值域w
。typedef struct AdjNode{ //定义邻接点类型
int v; //邻接点下标
struct AdjNode *next; //指向下一个邻接点
}AdjNode;
data
和指向第1个邻接点的指针first
typedef char VexType;//顶点的数据类型为字符型
typedef struct VexNode{ //定义顶点类型
VexType data; // VexType为顶点的数据类型,根据需要定义
AdjNode *first; //指向第一个邻接点
}VexNode;
typedef struct{//定义邻接表类型
VexNode Vex[MaxVnum];
int vexnum,edgenum; //顶点数,边数
}ALGraph;
Vex[]
的data
域中,将Vex[]
的first
域置空;Vex[]
中存储的下标 i i i、 j j j,创建一个新的邻接点 s s s,令s -> v = j
,s -> next = NULL
;然后将节点 s s s插入第 i i i个节点的第1个邻接点之前(头插法)。在无向图中,从节点 a a a到节点 b b b有边,从节点 b b b到节点 a a a也有边,因此还需要创建一个新的邻接点 s 2 s_{2} s2 。令s2 -> v = i
;s2->next = NULL
;然后将 s 2 s_2 s2节点插入第 j j j个节点的第1个邻接点之前(头插法)。Vex[]
中存储的下标 i i i、 j j j,创建一个新的邻接点 s s s,令s -> v = j
,s -> next = NULL
;然后将节点 s s s插入第 i i i个节点的第1个邻接点之前(头插法)。#include
using namespace std;
#define MaxVnum 100
typedef char VexType;//顶点的数据类型为字符型
typedef struct AdjNode{ //定义邻接点类型
int v; //邻接点下标
struct AdjNode *next; //指向下一个邻接点
}AdjNode;
typedef struct VexNode{ //定义顶点类型
VexType data; // VexType为顶点的数据类型,根据需要定义
AdjNode *first; //指向第一个邻接点
}VexNode;
typedef struct{//定义邻接表类型
VexNode Vex[MaxVnum];
int vexnum,edgenum; //顶点数,边数
}ALGraph;
int locatevex(ALGraph G,VexType x); //查找顶点信息的下标
void insertedge(ALGraph &G,int i,int j); //插入一条边
void CreateALGraph(ALGraph &G); //创建有向图的邻接表
void printg(ALGraph G); //输出邻接表
int main(){
ALGraph cs;
CreateALGraph(cs);
printg(cs);
}
int locatevex(ALGraph G,VexType x){
for(int i=0;i<G.vexnum;i++)
if(x==G.Vex[i].data)
return i;
return -1;//没找到
}
void insertedge(ALGraph &G,int i,int j){
AdjNode *s;
s=new AdjNode;
s->v=j;
s->next=G.Vex[i].first;
G.Vex[i].first=s;
}
void CreateALGraph(ALGraph &G){
int i,j;
VexType u,v;
cout<<"请输入顶点数和边数:"<<endl;
cin>>G.vexnum>>G.edgenum;
cout<<"请输入顶点信息:"<<endl;
for(i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
cin>>G.Vex[i].data;
for(i=0;i<G.vexnum;i++)
G.Vex[i].first=NULL;
cout<<"请依次输入每条边的两个顶点u,v"<<endl;
while(G.edgenum--){
cin>>u>>v;
i=locatevex(G,u);//查找顶点u的存储下标
j=locatevex(G,v);//查找顶点v的存储下标
if(i!=-1&&j!=-1)
insertedge(G,i,j);
else{
cout<<"输入顶点信息错!请重新输入!"<<endl;
G.edgenum++;//本次输入不算
}
}
}
void printg(ALGraph G){
for(int i=0;i<G.vexnum;i++){
AdjNode *t=G.Vex[i].first;
cout<<G.Vex[i].data<<": ";
while(t!=NULL){
cout<<"["<<t->v<<"]\t";
t=t->next;
}
cout<<endl;
}
}
输入:
5 7
a b c d e
a b
a c
a e
b c
c d
c e
d e
输出:
a: [4] [2] [1]
b: [2]
c: [4] [3]
d: [4]
e:
edge[]
,edge[i]
表示第 i i i条边。头节点数组:head[]
,head[i]
存储以 i i i为起点的第1条边的下标(edge[]
中的下标)const int maxe = 100;
const int maxn = 10;
struct node{
int to,next,w;
}ledge[maxe]; //边集数组,对边数一般要设置比maxn * maxn大的数
int head[maxn]; //头节点数组
#include
#include
using namespace std;
const int maxe = 100;
const int maxn = 10;
struct node{
int to,next,w;
}ledge[maxe]; //边集数组,对边数一般要设置比maxn * maxn大的数
int head[maxn]; //头节点数组
void add(int u,int v,int w); //添加一条边
void show(int u); //访问一个节点u的所有邻接点
int cnt = 0;
int main(){
int u,v,w,x;
memset(head,-1, sizeof(head)); //初始化头节点数组为-1
cout << "请输入边数:";
cin >> x;
while (x--){
cin >> u >> v >> w;
add(u,v,w);
add(v,u,w); //无向图一次需要添加两条边
}
cout << "请输入需要查询的节点:";
int u1;
cin >> u1;
show(u1);
}
void add(int u,int v,int w){
ledge[cnt].to = v;
ledge[cnt].w = w;
ledge[cnt].next = head[u];
head[u] = cnt++;
}
void show(int u){
cout << u << "的邻接点: ";
for (int i = head[u]; i!=-1 ; i = ledge[i].next) {
int v = ledge[i].to; //u的邻接点
cout << v << " ";
}
cout << endl;
cout << "对应权值:";
for (int i = head[u]; i!=-1 ; i = ledge[i].next) {
int w = ledge[i].w; //u~v的权值
cout << w << " ";
}
}
输入:
请输入边数:5
1 2 5
1 4 3
2 3 8
2 4 12
3 4 9
请输入需要查询的节点:2
输出:
2的邻接点: 4 3 1
对应权值:12 8 5
i^1
。(^
为异或运算符)给定有 N N N个节点、 M M M条边的有向图,对于每个节点 v v v都求 A ( v ) A(v) A(v),表示从节点 v v v出发,能到达的编号最大的节点。
输入:第1行包含两个整数 N N N、 M M M( 1 ≤ N 1 \leq N 1≤N, M ≤ 1 0 5 M \leq 10^5 M≤105).接下来的 M M M行,每行都包含两个整数 U i U_{i} Ui、 V i V_{i} Vi,表示边 ( U 1 , V i ) (U_{1},V_{i}) (U1,Vi)。节点的编号为 1 ∼ N 1 \sim N 1∼N。
输出: N N N个整数 A ( 1 ) A(1) A(1), A ( 2 ) A(2) A(2),… A ( N ) A(N) A(N)
#include
using namespace std;
const int maxn=100000+5;
int maxx[maxn],head[maxn];
int n,m,x,y,cnt;
struct Edge{
int to,next;
}e[maxn];
void add(int u,int v){//添加一条边u--v
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
void dfs(int u,int v){
if(maxx[v])
return;
maxx[v]=u;
for(int i=head[v];~i;i=e[i].next){
int v1=e[i].to;
dfs(u,v1);
}
}
int main(){
cin>>n>>m;
memset(head,-1,sizeof(head));
memset(maxx,0,sizeof(maxx));
for(int i=1;i<=m;i++){
cin>>x>>y;
add(y,x);//添加反向边
}
for(int i=n;i;i--)//倒序深度遍历
dfs(i,i);
for(int i=1;i<=n;i++){
if(i!=1)
cout<<" ";
cout<<maxx[i];
}
return 0;
}
输入:
4 3
1 2
2 4
4 3
输出:
4 4 3 4
有向图 D D D有 n n n个节点和 m m m条边,可以通过以下方式制作 D D D的Lying图 E E E。 E E E将有 m m m个节点,每个都用于表示 D D D的每条边
输入:第1行包含测试用例数 N ( N < 220 ) N(N < 220) N(N<220)。在每个测试用例的前两行都包含 m ( 0 ≤ m ≤ 300 ) m(0 \leq m \leq 300) m(0≤m≤300)和 k k k,表示图 E E E中的节点数和边数。下面的 k k k行,每行都包含两个节点 x x x和 y y y,表示在E中从 x x x到 y y y有一条边。节点编号为 0 ∼ m − 1 0 \sim m-1 0∼m−1。
输出:对每个测试用例,都输出一行Case #t,其中t表示测试用例编号,然后是Yes或者No,用于判断 E E E是否是一个有向图 D D D的Lying图。
#include
#define REP(i,b,e) for(int i=(b);i<(e);i++)
using namespace std;
const int maxn=300+5;
int g[maxn][maxn],n,m;
bool solve(){
REP(i,0,n)
REP(j,0,n){
bool flag1=false,flag2=false;
REP(k,0,n){
if(g[i][k]&&g[j][k]) //节点i和j有公共邻接点k
flag1=true;
if(g[i][k]^g[j][k]) //节点i与k邻接,节点j与k邻接,两者只有一个是真
flag2=true;
}
if(flag1&&flag2)
return false;
}
return true;
}
int main(){
int T,cnt=0,x,y;
cin>>T;
while(T--){
memset(g,0,sizeof(g));//初始化邻接矩阵
cin>>n>>m;
REP(i,0,m){
cin>>x>>y;
g[x][y]=1;
}
if(solve())
cout<<"Case #"<<++cnt<<": Yes"<<endl;
else
cout<<"Case #"<<++cnt<<": No"<<endl;
}
return 0;
}
输入:
4
2
1
0 1
5
0
4
3
0 1
2 1
2 3
3
9
0 1
0 2
1 2
1 0
2 0
2 1
0 0
1 1
2 2
输出:
Case #1: Yes
Case #2: Yes
Case #3: No
Case #4: Yes
约翰想安装奶牛的产奶能力给它们排序。已知有 N ( 1 ≤ N ≤ 1000 ) N(1 \leq N \leq 1000) N(1≤N≤1000)头奶牛,而且知道这些奶牛的 M ( 1 ≤ M ≤ 10000 ) M(1 \leq M \leq 10000) M(1≤M≤10000)种关系,将每种关系都表示为“ X X X Y Y Y”,表示奶牛 X X X的产奶能力大于奶牛 Y Y Y。约翰想知道自己至少还要调查多少对关系才能完成整个排序。
输入:第1行包含两个整数 N N N和 M M M。第 2... M + 1 2...M+1 2...M+1行,每行都包含两个整数 X X X和 Y Y Y。 X X X和 Y Y Y都在 1 ∼ N 1 \sim N 1∼N范围内,表示奶牛 X X X的排名高于奶牛 Y Y Y。
输出:单行输出至少还要调查多少种关系才能完成整个排序。
bitset
位运算,得到已知关系,将每个节点都用一个bitset
来表示。bitsetp[maxn]
其中maxn
表示位数,p[]
表示二进制数组。p[i][j] = 1
,即p[i]
的第 i i i位为1(从右侧数第0位、1位、2位)p[1][5] = 1
,则p[1] = ......100010
;p[1][4] = 1
,则p[1] = ......110010
;p[2][1] = 1
,则p[2] = ......000110
;p[2][3] = 1
,则p[2] = ......001110
;p[3][4] = 1
,则p[1] = ......011000
;p[2][1] = 1
,则p[2] = p[2] | p[1] = 001110 | 110010 = 111110
。如果2和1有关系,而1和4、5有关系,则通过或运算,可以得出2和4、5也有关系。ans
累计每个数组元素1的个数,因为初始化时自己到自己为1(p[1]
初始化时第1位为1),所以ans
多算了n
种关系,已知关系数应为ans-n
,用n(n-1)/2
减去已知关系数即可。#include
#include
using namespace std;
const int maxn=1000+5;
bitset<maxn>p[maxn]; //其中maxn表示位数,p[]表示二进制数组
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
p[i][i]=1;
while(m--){
int u,v;
cin>>u>>v;
p[u][v]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(p[i][k])
p[i]|=p[k]; //按位或运算
int ans=0;
for(int i=1;i<=n;i++)
ans+=p[i].count(); \\统计每个数组中元素1的个数
cout<<n*(n-1)/2-ans+n<<endl;
return 0;
}
输入:
5 5
2 1
1 5
2 3
1 4
3 4
输出:
3
#include
#include //引入队列头文件
using namespace std;
const int MaxVnum=100;//顶点数最大值
bool visited[MaxVnum]; //访问标志数组,其初值为"false"
typedef char VexType; //顶点的数据类型,根据需要定义
typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct{
VexType Vex[MaxVnum];
EdgeType Edge[MaxVnum][MaxVnum];
int vexnum,edgenum; //顶点数,边数
}AMGraph;
int locatevex(AMGraph G,VexType x){
for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
if(x==G.Vex[i])
return i;
return -1;//没找到
}
void CreateAMGraph(AMGraph &G){//创建有向图的邻接矩阵
int i,j;
VexType u,v;
cout<<"请输入顶点数:"<<endl;
cin>>G.vexnum;
cout<<"请输入边数:"<<endl;
cin>>G.edgenum;
cout<<"请输入顶点信息:"<<endl;
for(int i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
cin>>G.Vex[i];
for(int i=0;i<G.vexnum;i++)//初始化邻接矩阵所有值为0,如果是网,则初始化邻接矩阵为无穷大
for(int j=0;j<G.vexnum;j++)
G.Edge[i][j]=0;
cout<<"请输入每条边依附的两个顶点:"<<endl;
while(G.edgenum--){
cin>>u>>v;
i=locatevex(G,u);//查找顶点u的存储下标
j=locatevex(G,v);//查找顶点v的存储下标
if(i!=-1&&j!=-1)
G.Edge[i][j]=1; //邻接矩阵储置1,若无向图G.Edge[i][j]=G.Edge[j][i]=1
else{
cout<<"输入顶点信息错!请重新输入!"<<endl;
G.edgenum++;//本次输入不算
}
}
}
void print(AMGraph G){//输出邻接矩阵
cout<<"图的邻接矩阵为:"<<endl;
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++)
cout<<G.Edge[i][j]<<"\t";
cout<<endl;
}
}
void BFS_AM(AMGraph G,int v){//基于邻接矩阵的广度优先遍历
int u,w;
queue<int>Q; //创建一个普通队列(先进先出),里面存放int类型
cout<<G.Vex[v]<<"\t";
visited[v]=true;
Q.push(v); //源点v入队
while(!Q.empty()){ //如果队列不空
u=Q.front();//取出队头元素赋值给u
Q.pop(); //队头元素出队
for(w=0;w<G.vexnum;w++){//依次检查u的所有邻接点
if(G.Edge[u][w]&&!visited[w]){//u、w邻接而且w未被访问
cout<<G.Vex[w]<<"\t";
visited[w]=true;
Q.push(w);
}
}
}
}
int main(){
int v;
VexType c;
AMGraph G;
CreateAMGraph(G);
print(G);
cout << "请输入遍历图的起始点:";
cin>>c;
v=locatevex(G,c);//查找顶点u的存储下标
if(v!=-1){
cout<<"广度优先搜索遍历图结果:"<<endl;
BFS_AM(G,v);
}
else
cout<<"输入顶点信息错!请重新输入!"<<endl;
return 0;
}
输入与输出:
请输入顶点数:
6
请输入边数:
9
请输入顶点信息:
1 2 3 4 5 6
请输入每条边依附的两个顶点:
1 2
1 3
3 2
2 4
4 3
3 5
5 4
4 6
5 6
图的邻接矩阵为:
0 1 1 0 0 0
0 0 0 1 0 0
0 1 0 0 1 0
0 0 1 0 0 1
0 0 0 1 0 1
0 0 0 0 0 0
请输入遍历图的起始点:1
广度优先搜索遍历图结果:
1 2 3 4 5 6
#include
#include //引入队列头文件
using namespace std;
const int MaxVnum=100;//顶点数最大值
bool visited[MaxVnum]; //访问标志数组,其初值为"false"
typedef char VexType;//顶点的数据类型为字符型
typedef struct AdjNode{ //定义邻接点类型
int v; //邻接点下标
struct AdjNode *next; //指向下一个邻接点
}AdjNode;
typedef struct VexNode{ //定义顶点类型
VexType data; // VexType为顶点的数据类型,根据需要定义
AdjNode *first; //指向第一个邻接点
}VexNode;
typedef struct{//定义邻接表类型
VexNode Vex[MaxVnum];
int vexnum,edgenum; //顶点数,边数
}ALGraph;
int locatevex(ALGraph G,VexType x){
for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
if(x==G.Vex[i].data)
return i;
return -1;//没找到
}
void insertedge(ALGraph &G,int i,int j){//插入一条边
AdjNode *s;
s=new AdjNode;
s->v=j;
s->next=G.Vex[i].first;
G.Vex[i].first=s;
}
void printg(ALGraph G){//输出邻接表
cout<<"----------邻接表如下:----------"<<endl;
for(int i=0;i<G.vexnum;i++){
AdjNode *t=G.Vex[i].first;
cout<<G.Vex[i].data<<": ";
while(t!=NULL){
cout<<"["<<t->v<<"] ";
t=t->next;
}
cout<<endl;
}
}
void CreateALGraph(ALGraph &G){//创建有向图邻接表
int i,j;
VexType u,v;
cout<<"请输入顶点数和边数:"<<endl;
cin>>G.vexnum>>G.edgenum;
cout<<"请输入顶点信息:"<<endl;
for(i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
cin>>G.Vex[i].data;
for(i=0;i<G.vexnum;i++)
G.Vex[i].first=NULL;
cout<<"请依次输入每条边的两个顶点u,v"<<endl;
while(G.edgenum--){
cin>>u>>v;
i=locatevex(G,u);//查找顶点u的存储下标
j=locatevex(G,v);//查找顶点v的存储下标
if(i!=-1&&j!=-1)
insertedge(G,i,j);
else{
cout<<"输入顶点信息错!请重新输入!"<<endl;
G.edgenum++;//本次输入不算
}
}
}
void BFS_AL(ALGraph G,int v){//基于邻接表的广度优先遍历
int u,w;
AdjNode *p;
queue<int>Q; //创建一个普通队列(先进先出),里面存放int类型
cout<<G.Vex[v].data<<"\t";
visited[v]=true;
Q.push(v); //源点v入队
while(!Q.empty()){ //如果队列不空
u=Q.front();//取出队头元素赋值给u
Q.pop(); //队头元素出队
p=G.Vex[u].first;
while(p){//依次检查u的所有邻接点
w=p->v;//w为u的邻接点
if(!visited[w]){//w未被访问
cout<<G.Vex[w].data<<"\t";
visited[w]=true;
Q.push(w);
}
p=p->next;
}
}
}
void BFS_AL(ALGraph G){//非连通图,基于邻接表的广度优先遍历
for(int i=0;i<G.vexnum;i++)//非连通图需要查漏点,检查未被访问的顶点
if(!visited[i])//i未被访问,以i为起点再次广度优先遍历
BFS_AL(G,i);
}
int main(){
ALGraph G;
int v;
VexType c;
CreateALGraph(G);//创建有向图邻接表
printg(G);//输出邻接表
cout<<"请输入遍历图的起始点:";
cin>>c;
v=locatevex(G,c);//查找顶点u的存储下标
if(v!=-1){
cout<<"广度优先搜索遍历图结果:"<<endl;
BFS_AL(G,v);
}
else
cout<<"输入顶点信息错!请重新输入!"<<endl;
return 0;
}
输入与输出:
请输入顶点数和边数:
6 9
请输入顶点信息:
1 2 3 4 5 6
请依次输入每条边的两个顶点u,v
1 2
1 3
3 2
2 4
4 3
3 5
5 4
4 6
5 6
----------邻接表如下:----------
1: [2] [1]
2: [3]
3: [4] [1]
4: [5] [2]
5: [5] [3]
6:
请输入遍历图的起始点:1
广度优先搜索遍历图结果:
1 3 2 5 4 6
#include
using namespace std;
const int MaxVnum=100; //顶点数最大值
bool visited[MaxVnum]; //访问标志数组,其初值为"false"
typedef char VexType; //顶点的数据类型,根据需要定义
typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct{
VexType Vex[MaxVnum];
EdgeType Edge[MaxVnum][MaxVnum];
int vexnum,edgenum; //顶点数,边数
}AMGraph;
int locatevex(AMGraph G,VexType x){
for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
if(x==G.Vex[i])
return i;
return -1;//没找到
}
void CreateAMGraph(AMGraph &G){//创建无向图的邻接矩阵
int i,j;
VexType u,v;
cout<<"请输入顶点数:"<<endl;
cin>>G.vexnum;
cout<<"请输入边数:"<<endl;
cin>>G.edgenum;
cout<<"请输入顶点信息:"<<endl;
for(int i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
cin>>G.Vex[i];
for(int i=0;i<G.vexnum;i++)//初始化邻接矩阵所有值为0,如果是网,则初始化邻接矩阵为无穷大
for(int j=0;j<G.vexnum;j++)
G.Edge[i][j]=0;
cout<<"请输入每条边依附的两个顶点:"<<endl;
while(G.edgenum--){
cin>>u>>v;
i=locatevex(G,u);//查找顶点u的存储下标
j=locatevex(G,v);//查找顶点v的存储下标
if(i!=-1&&j!=-1)
G.Edge[i][j]=G.Edge[j][i]=1; //若有向图G.Edge[i][j]=1
else{
cout<<"输入顶点信息错!请重新输入!"<<endl;
G.edgenum++;//本次输入不算
}
}
}
void print(AMGraph G){//输出邻接矩阵
cout<<"图的邻接矩阵为:"<<endl;
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++)
cout<<G.Edge[i][j]<<"\t";
cout<<endl;
}
}
void DFS_AM(AMGraph G,int v){//基于邻接矩阵的深度优先遍历
int w;
cout<<G.Vex[v]<<"\t";
visited[v]=true;
for(w=0;w<G.vexnum;w++)//依次检查v的所有邻接点
if(G.Edge[v][w]&&!visited[w])//v、w邻接而且w未被访问
DFS_AM(G,w);//从w顶点开始递归深度优先遍历
}
int main(){
int v;
VexType c;
AMGraph G;
CreateAMGraph(G);//创建无向图的邻接矩阵
print(G);
cout<<"请输入遍历连通图的起始点:";
cin>>c;
v=locatevex(G,c);//查找顶点u的存储下标
if(v!=-1){
cout<<"深度优先搜索遍历连通图结果:"<<endl;
DFS_AM(G,v);
}
else
cout<<"输入顶点信息错!请重新输入!"<<endl;
return 0;
}
输入与输出:
请输入顶点数:
8
请输入边数:
9
请输入顶点信息:
1 2 3 4 5 6 7 8
请输入每条边依附的两个顶点:
1 3
1 2
2 6
2 5
2 4
3 8
3 7
4 5
7 8
图的邻接矩阵为:
0 1 1 0 0 0 0 0
1 0 0 1 1 1 0 0
1 0 0 0 0 0 1 1
0 1 0 0 1 0 0 0
0 1 0 1 0 0 0 0
0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 1
0 0 1 0 0 0 1 0
请输入遍历连通图的起始点:1
深度优先搜索遍历连通图结果:
1 2 4 5 6 3 7 8
#include
using namespace std;
const int MaxVnum=100; //顶点数最大值
bool visited[MaxVnum]; //访问标志数组,其初值为"false"
typedef char VexType; //顶点的数据类型为字符型
typedef struct AdjNode{ //定义邻接点类型
int v; //邻接点下标
struct AdjNode *next; //指向下一个邻接点
}AdjNode;
typedef struct VexNode{ //定义顶点类型
VexType data; // VexType为顶点的数据类型,根据需要定义
AdjNode *first; //指向第一个邻接点
}VexNode;
typedef struct{//定义邻接表类型
VexNode Vex[MaxVnum];
int vexnum,edgenum; //顶点数,边数
}ALGraph;
int locatevex(ALGraph G,VexType x){
for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
if(x==G.Vex[i].data)
return i;
return -1;//没找到
}
void insertedge(ALGraph &G,int i,int j){//插入一条边
AdjNode *s;
s=new AdjNode;
s->v=j;
s->next=G.Vex[i].first;
G.Vex[i].first=s;
}
void printg(ALGraph G){//输出邻接表
cout<<"----------邻接表如下:----------"<<endl;
for(int i=0;i<G.vexnum;i++){
AdjNode *t=G.Vex[i].first;
cout<<G.Vex[i].data<<": ";
while(t!=NULL){
cout<<"["<<t->v<<"] ";
t=t->next;
}
cout<<endl;
}
}
void CreateALGraph(ALGraph &G){//创建无向图邻接表
int i,j;
VexType u,v;
cout<<"请输入顶点数和边数:"<<endl;
cin>>G.vexnum>>G.edgenum;
cout<<"请输入顶点信息:"<<endl;
for(i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
cin>>G.Vex[i].data;
for(i=0;i<G.vexnum;i++)
G.Vex[i].first=NULL;
cout<<"请依次输入每条边的两个顶点u,v"<<endl;
while(G.edgenum--){
cin>>u>>v;
i=locatevex(G,u);//查找顶点u的存储下标
j=locatevex(G,v);//查找顶点v的存储下标
if(i!=-1&&j!=-1){
insertedge(G,i,j);
insertedge(G,j,i);//无向图多插入一条边
}
else{
cout<<"输入顶点信息错!请重新输入!"<<endl;
G.edgenum++;//本次输入不算
}
}
}
void DFS_AL(ALGraph G,int v){//基于邻接表的深度优先遍历
int w;
AdjNode *p;
cout<<G.Vex[v].data<<"\t";
visited[v]=true;
p=G.Vex[v].first;
while(p){//依次检查v的所有邻接点
w=p->v;//w为v的邻接点
if(!visited[w])//w未被访问
DFS_AL(G,w);//从w出发,递归深度优先遍历
p=p->next;
}
}
void DFS_AL(ALGraph G){//非连通图,基于邻接表的深度优先遍历
for(int i=0;i<G.vexnum;i++)//非连通图需要查漏点,检查未被访问的顶点
if(!visited[i])//i未被访问,以i为起点再次深度优先遍历
DFS_AL(G,i);
}
int main(){
ALGraph G;
int v;
VexType c;
CreateALGraph(G);//创建无向图的邻接表
printg(G);//输出邻接表
cout<<"请输入遍历连通图的起始点:";
cin>>c;
v=locatevex(G,c);//查找顶点u的存储下标
if(v!=-1){
cout<<"深度优先搜索遍历连通图结果:"<<endl;
DFS_AL(G,v);
}
else
cout<<"输入顶点信息错!请重新输入!"<<endl;
return 0;
}
输入与输出:
请输入顶点数和边数:
8 9
请输入顶点信息:
1 2 3 4 5 6 7 8
请依次输入每条边的两个顶点u,v
1 3
1 2
2 6
2 5
2 4
3 8
3 7
4 5
7 8
----------邻接表如下:----------
1: [1] [2]
2: [3] [4] [5] [0]
3: [6] [7] [0]
4: [4] [1]
5: [3] [1]
6: [1]
7: [7] [2]
8: [6] [2]
请输入遍历连通图的起始点:1
深度优先搜索遍历连通图结果:
1 2 4 5 6 3 7 8
某石油勘探公司正在按疾患勘探地下油田资源,在一片长方形地域中工作。他们首先将该地域划分为许多小正方形区域,然后使用勘探设备分别探测在每一小正方形区域内是否有油。含有油的区域被称为油田。如果两个油田相邻(在水平、垂直或对角线相邻),则它们是相同油藏的一部分。油藏可能非常大并可能包含许多油田(油田的个数不超过100)。你的工作是确定在这个长方形地域中包含多少不同的油藏。
输入:输入文件包含一个或多个长方形地域。每个地域的第1行都有两个正整数 m m m和 n ( 1 ≤ m , n ≤ 100 ) n(1 \leq m,n \leq 100) n(1≤m,n≤100),表示地域的行数和列数。如果 m = 0 m = 0 m=0,则表示输入结束;否则此后有 m m m行,每行都有 n n n个字符。每个字符都对应一个正方形区域,字符*表示没有油,字符@表示有油。
输出:对于每个长方形地域,都单行输出油藏的个数。
#include
#define REP(i,b,e) for(int i=(b);i<=(e);i++)
using namespace std;
const int maxn=100+5;
string str[maxn];//存储字符矩阵
int m,n,setid[maxn][maxn];//行列,连通分量号
void dfs(int x,int y,int id)//行列和连通分量号
{
if(x<0||x>=m||y<0||y>=n) return ;//出界
if(setid[x][y]>0||str[x][y]!='@') return ;//已有连通分量号或不是'@'
setid[x][y]=id;
REP(dx,-1,1)
REP(dy,-1,1)
if(dx!=0||dy!=0)
dfs(x+dx,y+dy,id);//八个方向深搜
}
int main()
{
while((cin>>m>>n)&&m&&n)
{
REP(i,0,m-1)
cin>>str[i];
memset(setid,0,sizeof(setid));
int cnt=0;
REP(i,0,m-1)
REP(j,0,n-1)
if(setid[i][j]==0&&str[i][j]=='@')
dfs(i,j,++cnt);
cout<<cnt<<endl;
}
return 0;
}
输入:
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0
输出:
0
1
2
2
给定一个有 n n n个节点、 m m m条边的无向图,每条边都涂有1种颜色。求节点1到 n n n的一条路径,使得经过的边数最少,在此前提下,经过边的颜色序列最小。可能有自环与重边。输入保证至少存在一条连接节点1和 n n n的路径。
输入:输入共 m + 1 m+1 m+1行。第1行包含两个整数: n n n和 m m m。之后的 m m m行,每行都包含3个整数 a i a_{i} ai、 b i b_{i} bi、 c i c_{i} ci,表示在 a i a_{i} ai、 b i b_{i} bi之间有一条颜色为 c i c_{i} ci的路径。
输出:输出共两行,第1行包含正整数 k k k,表示节点1到 n n n至少需要经过 k k k条边。第2行包含 k k k个由空格隔开的正整数,表示节点1到 n n n依次经过的边的颜色。
#include
using namespace std;
const int N=100000+5,M=200000+5,inf=0x7fffffff;
int n,m,cnt;
int head[N],dis[N];
bool vis[N];
queue<int>q1,q2,q3;
struct Edge{
int to,c,next;
}e[M];
void add(int u,int v,int c){//添加一条边
// e[++cnt].to=v;
// e[cnt].c=c;
// e[cnt].next=head[u];
e[++cnt]=(Edge){v,c,head[u]};
head[u]=cnt;
}
void bfs1(){//逆向标高求最短距离
int u,v;
memset(vis,false,sizeof(vis));
dis[n]=0;
q1.push(n);
vis[n]=true;
while(!q1.empty()){
u=q1.front();
q1.pop();
vis[u]=true;
for(int i=head[u];i;i=e[i].next){
v=e[i].to;
if(vis[v])
continue;
dis[v]=dis[u]+1;
q1.push(v);
vis[v]=true;
}
}
}
void bfs2(){
int u,v,minc,c;
bool first=true;
memset(vis,false,sizeof(vis));
vis[1]=true;
for(int i=head[1];i;i=e[i].next)//1号结点所有邻接点
if(dis[e[i].to]==dis[1]-1){//高度减1的邻接点
q1.push(e[i].to);
q2.push(e[i].c);
}
while(!q1.empty()){
minc=inf;
while(!q1.empty()){
v=q1.front();
q1.pop();
c=q2.front();
q2.pop();
if(c<minc){
while(!q3.empty())//发现更小队列清空
q3.pop();
minc=c;
}
if(c==minc)
q3.push(v);
}
if(first)
first=false;
else
cout<<" ";
cout<<minc;
while(!q3.empty()){//所有等于最小色号的结点
u=q3.front();
q3.pop();
if(vis[u])
continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].next){//扩展每一个结点
v=e[i].to;
if(dis[v]==dis[u]-1){
q1.push(v);
q2.push(e[i].c);
}
}
}
}
}
int main(){
int u,v,c;
while(cin>>n>>m){
memset(head,0,sizeof(head));
cnt=0;
for(int i=1;i<=m;i++){
cin>>u>>v>>c;
add(u,v,c);
add(v,u,c);
}
bfs1();
cout<<dis[1]<<endl;
bfs2();
cout<<endl;
}
return 0;
}
输入:
4 6
1 2 1
1 3 2
3 4 3
2 3 1
2 4 4
3 1 1
输出:
2
1 3
骑士决定环游世界,其移动方式如下图。骑士的世界是他生活的棋盘,棋盘面积比普通的 8 × 8 8 \times 8 8×8棋盘小,但它任然是长方形的。你能帮助这个骑士做出旅行计划吗?找到一条道路。骑士每次都进入一个方格,可以在棋盘的任意方格上开始和结束。
输入:输入的第1行包含一个正整数 T T T,表示测试用例的数量。每个测试用例的第1行都包含两个 m m m和 n ( 1 ≤ m × n ≤ 26 ) n(1 \leq m \times n \leq 26) n(1≤m×n≤26),表示 m × n m \times n m×n的棋盘,对行数字标识( 1 ∼ m 1 \sim m 1∼m),对列用大写字母标识( A ∼ Z A \sim Z A∼Z)。
输出:每个测试用例的输出都以一个包含“$Scenario #i: $”的行开头,其中i是从1开始的测试用例编号。然后单行输出按字典顺序排列的第1条路径,该路径访问棋盘的所有方块。应通过连接访问方块的名称输出路径,每个方块的名称都由一个大写字母后跟一个数字组成。如果不存在这样的路径,则应该在一行上输出“impossible”。在测试用例之间有个空行。
#include
#include
using namespace std;
bool map[30][30],flag;
int dir[8][2]={-2,-1,-2,1,-1,-2,-1,2,1,-2,1,2,2,-1,2,1};
int path[30][2];
int n,m;
int dfs(int x,int y,int step); //深度搜索
int main()
{
int T;
cin>>T;
for(int k=1;k<=T;k++)
{
memset(map,0,sizeof(map));
cin>>m>>n;
flag=0;
cout<<"Scenario #"<<k<<":"<<endl;
path[0][0]=1;
path[0][1]=1;
map[1][1]=1;
if(dfs(1,1,1))
{
for(int i=0;i<m*n;i++)
cout<<char(path[i][0]+'A'-1)<<path[i][1];
cout<<endl<<endl;
}
else
cout<<"impossible"<<endl<<endl;
}
return 0;
}
int dfs(int x,int y,int step)
{
if(step==n*m)
return flag=1;
for(int i=0;i<8;i++)
{
int x2=x+dir[i][0];
int y2=y+dir[i][1];
if(x2>=1&&x2<=n&&y2>=1&&y2<=m&&!map[x2][y2]&&!flag)
{
map[x2][y2]=1;
path[step][0]=x2;
path[step][1]=y2;
dfs(x2,y2,step+1);
map[x2][y2]=0;
}
}
return flag;
}
输入:
3
1 1
2 3
4 3
输出:
Scenario #1:
A1
Scenario #2:
impossible
Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4
约翰希望立即抓住逃亡的牛。当前约翰在节点 N N N,牛在节点 K ( 0 ≤ N , K ≤ 100000 ) K(0 \leq N,K \leq 100000) K(0≤N,K≤100000)时,他们在同一条线上。约翰有两种交通方式:步行和乘车。如果牛不知道有人在追赶自己,原地不动,那么约翰需要多长时间才能抓住牛?
输入:两个整数 N N N和 K K K。
输出:单行输出约翰抓住牛所需的最短时间(以分钟为单位)。
dfs(t)
表示求解约翰从初始位置 n n n到达位置 t t t的最小步数。dfs(t/2)
加上1次乘车所需步数,第2种方案的步数为 t − n t-n t−n。dfs(t-1)+1
)、从 t + 1 t+1 t+1向后1步到 t t t(步数为dfs(t+1)+1
),采用哪种方案使得步数最少,取最小值。#include
using namespace std;
int n,s;
int dfs(int t); //从n到达位置t的最小步数(深度搜索)
int main(){
scanf("%d %d",&n,&s);
if(n == 0){ //如果n = 0则先走一步到1,否则无法乘车
n++;
printf("%d",dfs(s)+1);
}
printf("%d",dfs(s));
return 0;
}
int dfs(int t){
if(t <= n){ //不能向后乘车,所以只能一步步倒退
return n - t;
}
if(t % 2 == 1){ //如果t为奇数,比较从t-1向前1步到t、从t+1向后1步到t哪种步数少
return min(dfs(t+1)+1,dfs(t-1)+1);
}
else{ //如果t为偶数,比较从t/2乘车到t、从n一步步走哪一种步数少
return min(dfs(t/2)+1,t-n);
}
}
输入:
5 17
输出:
4
#include
#include
using namespace std;
const int MAXN=100009;
bool vis[MAXN];
int d[MAXN];
int n,k;
void solve(); //广度优先搜索
int main(){
while(cin>>n>>k){
if(k<=n){
cout<<n-k<<endl;
continue;
}
solve();
}
return 0;
}
void solve(){
queue<int> q;
vis[n] = 1;
d[n] = 0;
q.push(n);
while(!q.empty()){
int u = q.front();
q.pop();
if(u == k){
cout << d[k] << endl;
return;
}
int x;
x = u + 1; //向前
if(x >= 0 && x <= 100000 && !vis[x]){ //未超界且未访问
d[x] = d[u] + 1;
vis[x] = 1;
q.push(x);
}
x = u - 1; //向后
if(x >= 0 && x <= 100000 && !vis[x]){ //未超界且未访问
d[x] = d[u] + 1;
vis[x] = 1;
q.push(x);
}
x = u * 2; //乘车
if(x >= 0 && x <= 100000 && !vis[x]){ //未超界且未访问
d[x] = d[u] + 1;
vis[x] = 1;
q.push(x);
}
}
}
输入:
5 17
输出:
4
dfn[u]
表示节点 u u u深度优先遍历的序号low[u]
表示节点 u u u或 u u u的子孙能通过非父子边追溯到的dfn
最小的节点序号,即回到最早的过去。#include
#include
using namespace std;
const int maxn=1000+5;
int n,m,head[maxn],cnt;
int low[maxn],dfn[maxn],num;
struct Edge{
int to,next;
}e[maxn<<1];
void add(int u,int v); //添加一条边
void tarjan(int u,int fa); //求桥
void init(); //数组初始化函数
int main(){
while(cin>>n>>m){ //输入节点数与边数
init();
int u,v; //输入节点间连接关系
while(m--){
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(1,0);
}
return 0;
}
void add(int u,int v){
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++num;
for(int i=head[u];i;i=e[i].next){ //遍历节点u的所有邻接点
int v=e[i].to;
if(v==fa)
continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]) //判断孩子的low值是否比自己的dfn值大
cout<<u<<"—"<<v<<"是桥"<<endl;
}
else
low[u]=min(low[u],dfn[v]);
}
}
void init(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
cnt=num=0;
}
输入:
7 7
1 2
2 3
3 5
5 6
6 4
4 1
5 7
输出:
5—7是桥
#include
#include
using namespace std;
const int maxn=1000+5;
int n,m,head[maxn],cnt,root;
int low[maxn],dfn[maxn],num;
struct Edge{
int to,next;
}e[maxn<<1]; //maxn << 1 = maxn * 2
void add(int u,int v); //添加一条边
void tarjan(int u,int fa); //求割点
void init(); //数组初始化
int main(){
while(cin>>n>>m){
init();
int u,v;
while(m--){
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i]){
root=i;
tarjan(i,0);
}
}
return 0;
}
void add(int u,int v){
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++num;
int count=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa)
continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
count++;
if(u!=root||count>1)
cout<<u<<"是割点"<<endl;
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
void init(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
cnt=num=0;
}
输入:
7 7
1 2
2 3
3 5
5 6
6 4
4 1
5 7
输出:
5是割点
dfn[x] = low[x] = ++num
。low[x] = min(low[x],low[y])
。low[x] = min(low[x],dfn[y])
。low[x] = dfn[x]
,则从栈中不断弹出节点,直到 x x x出栈时停止。弹出的节点就是一个连通分量。#include
#include
#include
using namespace std;
const int maxn=1000+5;
int n,m,head[maxn],cnt;
int low[maxn],dfn[maxn],num;
stack<int>s;
bool ins[maxn];
struct Edge{
int to,next;
}e[maxn<<1];
void add(int u,int v); //添加一条边
void tarjan(int u); //求强连通分量
void init(); //初始化数组
int main(){
while(cin>>n>>m){
init();
int u,v;
while(m--){
cin>>u>>v;
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
}
return 0;
}
void add(int u,int v){
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void tarjan(int u){
low[u]=dfn[u]=++num;
cout<<"low["<<u<<"]="<<low[u]<<"\tdfn["<<u<<"]="<<dfn[u]<<endl;
ins[u]=true;
s.push(u);
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
cout<<"update1:low["<<u<<"]="<<low[u]<<endl;
}
else if(ins[v]){
low[u]=min(low[u],dfn[v]);
cout<<"update2:low["<<u<<"]="<<low[u]<<endl;
}
}
if(low[u]==dfn[u]){
int v;
cout<<"强连通分量:";
do{
v=s.top();
s.pop();
cout<<v<<" ";
ins[v]=false;
}while(v!=u);
cout<<endl;
}
}
void init(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(ins,0,sizeof(ins));
cnt=num=0;
}
输入:
5 8
1 3
1 2
3 5
3 4
3 2
4 5
4 1
5 1
输出:
low[1]=1 dfn[1]=1
low[2]=2 dfn[2]=2
强连通分量:2
update1:low[1]=1
low[3]=3 dfn[3]=3
low[4]=4 dfn[4]=4
update2:low[4]=1
low[5]=5 dfn[5]=5
update2:low[5]=1
update1:low[4]=1
update1:low[3]=1
update2:low[3]=1
update1:low[1]=1
强连通分量:5 4 3 1
#训练1:电话网络
电话公司正在建立一个新的电话网络,每个地方都有一个电话交换机(编号为 1 ∼ N 1 \sim N 1∼N)。线路是双向的,并且总是将两个地方连接在一起,在每个地方,线路都终止于电话交换机。从每个地方都可以通过线路到达其他地方,但不需要直接连接,可以进行多次交换。有时在某个地方发生故障,会导致交换机无法运行。在这种情况下,除了无法到达失败的地方,还可以导致其他地方无法相互连接。这个地方(发生故障的地方)是至关重要的,请写程序来查找所有关键位置的数量。
输入:输入包含多个测试用例,每个测试用例都描述一个网络。每个测试用例的第1行都是 N ( N < 100 ) N(N < 100) N(N<100)。接下来最多 N N N行中的每一行都包含一个地点的编号,后面是该地方可以直达的地点的编号,每个测试用例都以一条仅包含0的行结束。 N = 0 N = 0 N=0时输入结束,不处理。
输出:对每个测试用例,都单行输出关键位置的数量。
#include
#include
#include
using namespace std;
const int maxn=105;
int n,root,head[maxn],cnt,low[maxn],dfn[maxn],num;
bool cut[maxn];
struct Edge{
int to,next;
}e[maxn*maxn];
void add(int u,int v); //添加一条边
void tarjan(int u); //求割点
void init(); //初始化数组
int main(){
while(cin>>n&&n){
init();
int u,v;
while(cin>>u&&u){
while(1){
char c=getchar();
if(c=='\n')
break;
cin>>v;
add(u,v);
add(v,u);
}
}
for(int i=1;i<=n;i++)
if(!dfn[i]){
root=i;
tarjan(i);
}
int ans=0;
for(int i=1;i<=n;i++)
if(cut[i])
ans++;
cout<<ans<<endl;
}
return 0;
}
void add(int u,int v){
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void tarjan(int u){
dfn[u]=low[u]=++num;
int flag=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
flag++;
if(u!=root||flag>1)//u不是根或者u是根但至少有两个子结点
cut[u]=true;
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
void init(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,false,sizeof(cut));
cnt=num=0;
}
输入:
5
5 1 2 3 4
0
6
2 1 3
5 4 6 2
0
0
输出:
1
2
热带岛屿负责道路的人们想修理和升级岛上各个旅游景点之间的道路。道路本身也很有趣,它们从不在交叉路口汇合,而是通过桥梁和隧道相互交叉或相互通过。通过这种方式,每条道路都在两个特定的旅游景点之间运行,这样游客就不会迷失。不幸的是,当建筑公司在特定道路上工作时,该道路在任何一个方向都无法使用。如果在两个旅游景点之间无法同行,则即使建筑公司在任何特定时间只在一条道路上工作,也可能出现问题。
道路部门已经决定在景点之间建造新的道路,以便在最终配置中,如果任何一条道路正在建设,则仍然可以使用剩余的道路在任意两个旅游景点之间旅行。我们的任务是找到所需的最少数量的新道路。
输入:输入的第1行将包括正整数 n n n( 3 ≤ n ≤ 1000 3 \leq n \leq 1000 3≤n≤1000)和 r r r( 2 ≤ r ≤ 1000 2 \leq r \leq 1000 2≤r≤1000),其中 n n n是旅游景点的数量, r r r是道路的数量。旅游景点的编号为 1 ∼ n 1 \sim n 1∼n。以下 r r r行中的每一行都将由两个整数 v v v和 w w w组成,表示在 v v v和 w w w的景点之间存在道路。请注意,道路是双向的,在任何两个旅游景点之间最多有一条道路。此外,在目前的配置中,可以在任意两个旅游景点之间旅行。
输出:单行输出需要添加的最少道路数量。
low[u]!=low[v]
,则将这个连通分量点low[u]
的度加1,degree[low[u]]++
,同一个连通分量中的节点low[]
相同。leaf
,添加的最少边数为(leaf + 1)/2
。#include
#include
using namespace std;
const int maxn=1000+5;
int n,m,head[maxn],cnt;;
int low[maxn],dfn[maxn],degree[maxn],num;
struct Edge{
int to,next;
}e[maxn<<1];
void add(int u,int v){
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void tarjan(int u,int fa){//求边双连通分量
dfn[u]=low[u]=++num;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa)
continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else
low[u]=min(low[u],dfn[v]);
}
}
void init(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(degree,0,sizeof(degree));
cnt=num=0;
}
int main(){
while(cin>>n>>m){
init();
int u,v;
while(m--){
cin>>u>>v;
add(u,v);
add(v,u);
}
tarjan(1,0);
for(int u=1;u<=n;u++)
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(low[u]!=low[v])
degree[low[u]]++;
}
int leaf=0;
for(int i=1;i<=n;i++)
if(degree[i]==1)
leaf++;
cout<<(leaf+1)/2<<endl;
}
return 0;
}
输入:
10 12
1 2
1 3
1 4
2 5
2 6
5 6
3 7
3 8
7 8
4 9
4 10
9 10
3 3
1 2
2 3
1 3
输出:
2
0