http://poj.org/problem?id=1523
居然1A and 0ms 太让我意外了
题目大意:
给你几个电脑的双相连通图 问你是否存在割点 如果存在输出割点并输出此割点见原图变成了几个块
输入输出只要注意就是了 没别的办法
Tarjan 算法 我就不多说了 我也说不好
总之用Tarjan算法找割点 但是你搜索时的根结点要特判
对不每个割点用dfs求其可把原图分成几个块
从割点发出可能用k个分支 那么块数 <= k
对其分支进行深搜并记录 有几个分支可以向下搜 就有几个块
深搜记录 可以搞定几个分支相连的情况
详情见代码注释:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
const int N=1005;
const int M=10000005;
struct node
{
struct tt *next;
}mem[N];
struct tt
{
struct tt *next;
int j;
};
int had[N];//割点是否已保存
int cutpoint[N];//保存割点
int I;//割点数目
int deep;//深度 (时间戳)
bool visited[N];
stack<int>str;
bool in[N];
int low[N];
int dfn[N];
int st;//开始搜的起始点
void build(int i,int j)
{
struct tt *t=new tt;
t->j=j;
t->next=mem[i].next;
mem[i].next=t;
}
void Clear()//每次对表头进行清理
{
for(int i=0;i<N;++i)
mem[i].next=NULL;
}
void Tarjan(int x)
{
++deep;
low[x]=dfn[x]=deep;
visited[x]=true;
str.push(x);
in[x]=true;
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(visited[t->j]==false)
{
Tarjan(t->j);
low[x]=min(low[x],low[t->j]);
}else if(in[t->j])
{
low[x]=min(low[x],dfn[t->j]);
}
if(!had[x]&&low[t->j]>=dfn[x]&&x!=st)//如果此点没记录 且为割点
{//但是不能等于起始点 因为起始点得进行特判
had[x]=true;
cutpoint[I]=x;
++I;
}
t=t->next;
}
if(low[x]==dfn[x])//对以x为根的强连通图进行出栈处理
{
while(str.top()!=x)
{
in[str.top()]=false;
str.pop();
}
in[str.top()]=false;
str.pop();
}
}
void dfs(int x)//深搜并记录
{
visited[x]=true;
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(visited[t->j]==false)
dfs(t->j);
t=t->next;
}
}
int findnum(int x)
{
memset(visited,false,sizeof(visited));
visited[x]=true;
struct tt *t=mem[x].next;
int num=0;
while(t!=NULL)
{
if(visited[t->j]==false)//有多少分支是可搜的
{
++num;
dfs(t->j);
}
t=t->next;
}
return num;
}
int main()
{
int i,j;
for(int w=1;;++w)
{
st=M;
while(scanf("%d",&i),i)
{
scanf("%d",&j);
if(st==M)
st=j;
build(i,j);
build(j,i);
}
if(st==M)
break;
I=0;
if(findnum(st)>1)//对起始点进行特判 看它有多少不相连分支
{
had[st]=true;
cutpoint[I]=st;
++I;
}
deep=0;
while(!str.empty())
str.pop();
memset(in,false,sizeof(in));
memset(visited,false,sizeof(visited));
memset(had,false,sizeof(had));
Tarjan(st);
printf("Network #%d\n",w);
if(I==0)
{
printf(" No SPF nodes\n");
}
else
{
sort(cutpoint,cutpoint+I);
for(int i=0;i<I;++i)
{
printf(" SPF node %d leaves %d subnets\n",cutpoint[i],findnum(cutpoint[i]));
}
}
printf("\n");
Clear();
}
return 0;
}
更令我意外的是 直接用dfs 就能过 时间 16ms
每次对每个点进行判定 如果从他出发的分支有多余一个是可搜的 则是割点 且有多少是可搜的 就有多少块
其实就是对上面程序中对起始点特判的方法 无语了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
const int N=1001;
const int M=10000005;
struct node
{
struct tt *next;
}mem[N];
struct tt
{
struct tt *next;
int j;
};
int I;//割点数目
bool visited[N];
bool had[N];
void build(int i,int j)
{
struct tt *t=new tt;
t->j=j;
t->next=mem[i].next;
mem[i].next=t;
}
void Clear()//每次对表头进行清理
{
for(int i=0;i<N;++i)
mem[i].next=NULL;
}
void dfs(int x)//深搜并记录
{
visited[x]=true;
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(visited[t->j]==false)
dfs(t->j);
t=t->next;
}
}
int findnum(int x)
{
memset(visited,false,sizeof(visited));
visited[x]=true;
struct tt *t=mem[x].next;
int num=0;
while(t!=NULL)
{
if(visited[t->j]==false)//有多少分支是可搜的
{
++num;
dfs(t->j);
}
t=t->next;
}
// cout<<x<<" "<<num<<endl;
return num;
}
int main()
{
int i,j;
for(int w=1;;++w)
{
memset(had,false,sizeof(had));
j=-1;
while(scanf("%d",&i),i)
{
scanf("%d",&j);
had[i]=had[j]=true;
build(i,j);
build(j,i);
}
if(j==-1)
break;
printf("Network #%d\n",w);
I=0;
for(int i=1;i<N;++i)
{
if(had[i]==false)
continue;
int k=findnum(i);
if(k>1)
{
++I;
printf(" SPF node %d leaves %d subnets\n",i,k);
}
}
if(I==0)
{
printf(" No SPF nodes\n");
}
printf("\n");
Clear();
}
return 0;
}