大体思路都是找强连通子图,缩点,只是两个题目的所求不是太一样。
hdu 3836:
参考了qianshou的代码,网上的很多看的不是很懂,他写的很清晰,很好看懂
http://blog.csdn.net/wsniyufang/article/details/6604503
然后就是还要了解一点Tarjan算法,下面的那个网址讲的很清楚
http://www.byvoid.com/blog/scc-tarjan/
剩下的就是要自己写一个getans()的求结果的程序。
经过缩点后,剩下的就是一个DAG图了。
至于为什么是取入度和出度为0的点中最大的,我想了很久,还是不是特别清楚。
我是这么想的,剩下的图如果要全联通,那么每个点必须有入度和出度,如果出度为0 的点多于入度为0的点。那么可以让度为0的点指向
入度为0的点和其他的点,从而让每个点都有入度和出度。反之亦然。
感觉是对的,但不知道这样想对不对,如果路过的大牛有好的观点,希望给予指正
/*
HDU 3836
题目大意:给出一个有向图,求最少添加多少条边使得该图变成强连通图。
首先如果图本身就是强连通那么答案为0。
否则先缩强连通分量将图变为DAG,然后算出入度为0的点和出度为0的点的个数,取最大值即为答案。
2011-7-14
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define mMax 50010
#define nMax 20010
using namespace std;
struct Edge //边
{
int v, x;
}edge[mMax];
int cnt, top, ans, n, m, e;
int belong[nMax], dfn[nMax], low[nMax], Stack[nMax], head[nMax], out[nMax], in[nMax];
bool instack[nMax];
void BuildGraph(int from, int to) //建图
{
edge[e].v=to;
edge[e].x=head[from];
head[from]=e;
e++;
}
void init() //初始化及输入
{
memset(head, -1, sizeof(head));
memset(belong, 0, sizeof(belong));
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(out, 0, sizeof(out));
memset(in, 0, sizeof(in));
memset(instack, 0, sizeof(instack));
int a, b; e=0;
for(int i=0; i<m; i++)
{
scanf("%d%d", &a, &b);
BuildGraph(a ,b);
}
}
int getans() //输出
{
int ct[nMax]; //ct[i]=j:强连通分支i有j个点
int v;
memset(ct, 0, sizeof(ct));
if(cnt==1) return 0;
//枚举每一个点,求outdegree和ct
for(int i=1; i<=n; i++)
{
ct[belong[i]]++;
for(int k=head[i]; k!=-1; k=edge[k].x)
{
v=edge[k].v;
if(belong[i]!=belong[v])
{
out[belong[i]]++; //出度
in[belong[v]]++; //入度
}
}
}
int num_in=0, num_out=0;
for(int i=1; i<=cnt; i++)
{
if(in[i]==0)
num_in++;
if(out[i]==0)
num_out++;
}
return max(num_out, num_in);//入度和出度中最大的
}
void dfs(int i)
{
int j;
dfn[i]=low[i]=ans++;
instack[i]=1;
Stack[++top]=i;
//对每条边枚举
for(int k=head[i]; k!=-1; k=edge[k].x)
{
j=edge[k].v;
//cout<<k<<endl;system("pause");
if(!dfn[j])
{
dfs(j);
low[i]=min(low[i], low[j]);
}
else if(instack[j] && dfn[j]<low[i])
low[i]=dfn[j];
}
//如果i是强连通分量的根
if(dfn[i]==low[i])
{
cnt++;
do
{
j=Stack[top--];
instack[j]=0;
belong[j]=cnt;
}while(j!=i);
}
}
void Tarjan() //Tarjan算法
{
top=cnt=0;ans=1;
memset(dfn, 0, sizeof(dfn));
for(int i=1; i<=n; i++)
if(!dfn[i])
dfs(i);
printf("%d\n", getans());
}
int main()
{
while(scanf("%d%d", &n,&m)!=EOF)
{
init();
Tarjan();
}
}
;-------------------------------------------------------------
POJ 2186
与上题的大体框架一样,我只改动了getans();
貌似这题的数据很弱,Discuss里还有一些数据,我也试了下,过了。
注意题目中有关系A->B,B->C可以得到A->C。
这题就是要找出度为0的点(将奶牛的关系用有向图表示),因为只能有一个,
所以当出现两个出度为0的点的时候表示至少有一个(一个集合)与当前的
奶牛合不来,没有最受欢迎的,无解。
而且,当只有一个出度为0 的点的时候,表示其他所有的奶牛都有指向此奶牛的路径,
因此,他是最受欢迎的。
另外,我走了一点弯路,有点想偷懒,想用instack[]数组标记有出度的点,但后来发
现一旦出现环路,就会得到一个错误的结论,贡献了两次WA
/*
题目大意:n头奶牛,给出若干个欢迎关系a b,表示a欢迎b,
欢迎关系是单向的,但是是可以传递的。另外每个奶牛都是欢
迎他自己的。求出被所有的奶牛欢迎的奶牛的数目。
2011-7-14
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define mMax 50010
#define nMax 20010
using namespace std;
struct Edge
{
int v, x;
}edge[mMax];
int cnt, top, ans, n, m, e;
int belong[nMax], dfn[nMax], low[nMax], Stack[nMax], head[nMax], out[nMax], in[nMax];
bool instack[nMax];
void BuildGraph(int from, int to)
{
edge[e].v=to;
edge[e].x=head[from];
head[from]=e;
e++;
}
void init()
{
memset(head, -1, sizeof(head));
memset(belong, 0, sizeof(belong));
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(out, 0, sizeof(out));
memset(in, 0, sizeof(in));
memset(instack, 0, sizeof(instack));
int a, b; e=0;
for(int i=0; i<m; i++)
{
scanf("%d%d", &a, &b);
BuildGraph(a ,b);
}
}
int getans()
{
memset(instack, 0, sizeof(instack));
int v;
if(cnt==1) return n;
for(int i=1; i<=n; i++)
{
for(int k=head[i]; k!=-1; k=edge[k].x)
{
v=edge[k].v;
if(belong[i]!=belong[v])
{
out[belong[i]]++;
}
}
}
int sum=0, num=0, flag=0;
for(int i=1; i<=cnt; i++)
{
if(out[i]==0)
{
flag=i;
num++;
}
}
if(num>1) return 0;
for(int i=1; i<=n; i++)
{
if(belong[i]==flag) sum++;
}
return sum;
}
void dfs(int i)
{
int j;
dfn[i]=low[i]=ans++;
instack[i]=1;
Stack[++top]=i;
//对每条边枚举
for(int k=head[i]; k!=-1; k=edge[k].x)
{
j=edge[k].v;
//cout<<k<<endl;system("pause");
if(!dfn[j])
{
dfs(j);
low[i]=min(low[i], low[j]);
}
else if(instack[j] && dfn[j]<low[i])
low[i]=dfn[j];
}
//如果i是强连通分量的根
if(dfn[i]==low[i])
{
cnt++;
do
{
j=Stack[top--];
instack[j]=0;
belong[j]=cnt;
}while(j!=i);
}
}
void Tarjan()
{
top=cnt=0;ans=1;
memset(dfn, 0, sizeof(dfn));
for(int i=1; i<=n; i++)
if(!dfn[i])
dfs(i);
printf("%d\n", getans());
}
int main()
{
while(scanf("%d%d", &n,&m)!=EOF)
{
init();
Tarjan();
}
return 0;
}