您的位置:首页 > 其它

hdu1827&&hdu2767----Kosaraju算法

2013-05-23 00:00 232 查看
/**
*  hdu1827
*  题意:给一个有向图,每个顶点有个权值,求入度为0的顶点的个数及从入度为0的顶点出发,
*  到达图的所有顶点的最小花费和
*  解法:缩点后,从入度为0的顶点在反向图中dfs一遍,求出路径上的顶点的最小花费
**/

#include <cstdio>
#include <cstring>
#include <climits>
#include <iostream>

using namespace std;

int scc[1005];  ///缩图,存强连通块
int dfn[1005];  ///存顶点访问顺序
int scc_root[1005]; ///存强连通分量中的某一个顶点
int ind[1005];  ///缩图,存入度数
bool vis[1005]; ///访问标记
int cost[1005]; ///花费
int sc, cnt, mincc;
int minp, minc;
///sc    对同一个强连通子图中的顶点标记为同一标志
///cnt   -----
///total 把每个强连通子图看成是一个点,统计其他点可到该点的数目
struct Node {
int u;
Node *next;
Node() {}
Node(int uu, Node *p=NULL) : u(uu), next(p){}
};
struct Graph {
Node *head;
Graph() {}
}G[1005], NG[1005];
void initG(int n) {///初始化
for (int i=1; i<=n; ++i) {
G[i].head = NULL;
NG[i].head = NULL;
}
}
void buildG(Graph g[], int v, int u) {///建图
Node *p = new Node(u);
p->next = g[v].head;
g[v].head = p;
}
void dfs_one(int k) {///第一次dfs求顶点访问顺序
vis[k] = true;
for (Node *p=G[k].head; p!=NULL; p=p->next) {
if (!vis[p->u]) {
dfs_one(p->u);
}
}
dfn[++cnt] = k;///存顶点访问顺序
}
void dfs_two(int k) {///第二次dfs缩图
scc[k] = sc;     ///同一个强连通分量标记为一样的标志
vis[k] = true;
for (Node *p=NG[k].head; p!=NULL; p=p->next) {
if (!vis[p->u]) {
dfs_two(p->u);
}
}
}
void dfs_three(int k) {///第三次dfs
vis[k] = true;
if (mincc > cost[k]) {///访问路径上的最小花费
mincc = cost[k];
}
for (Node *p=NG[k].head; p!=NULL; p=p->next) {
if (!vis[p->u])  {
dfs_three(p->u);
}
}
}
void kosaraju(int n) {
cnt = 0;
memset(vis, false, sizeof(vis));
for (int i=1; i<=n; ++i) {///求顶点访问顺序
if (!vis[i]) {
dfs_one(i);
}
}
memset(vis, false, sizeof(vis));
sc = 0;
for (int i=n; i>0; --i) {///缩图
if (!vis[dfn[i]]) {
++sc;
scc_root[sc] = dfn[i];
dfs_two(dfn[i]);
}
}

for (int i=1; i<=sc; ++i) {
ind[i] = 0;
}
for (int i=1; i<=n; ++i) {
for (Node *p=G[i].head; p!=NULL; p=p->next) {
if (scc[i] != scc[p->u]) {///不在同一个强连通内
++ind[scc[p->u]];
}
}
}

minc = 0;
minp = 0;
for (int i=1; i<=sc; ++i) {
if (ind[i] == 0) {///对入度为0的点进行第三次dfs
++minp;
mincc = INT_MAX;
memset(vis, false, sizeof(vis));
dfs_three(scc_root[i]);
minc += mincc;
///           minc += cost[scc_root[i]];
}
}
}
void del(Node *pNode) {
if (pNode != NULL) {
del(pNode->next);
delete pNode;
pNode = NULL;
}
}
void deleteG(Graph g[], int n) {///释放图
for (int i=1; i<=n; ++i) {
if (g[i].head != NULL) {
del(g[i].head);
}
}
}
int main(void) {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
initG(n);
int v, u;
for (int i=1; i<=n; ++i) {
scanf("%d", &cost[i]);
}
while (m--) {
scanf("%d%d", &v, &u);
buildG(G, v, u);
buildG(NG, u, v);
}
kosaraju(n);
deleteG(G, n);
deleteG(NG, n);

printf("%d %d\n", minp, minc);
}
return 0;
}

/**
* hdu2767
* 题意: 在一个有向图中,问至少加几条边让整个图变成强连通
* 解法:缩点后统计出度为0和入度为0的点的个数,两者取最大值
**/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

int dfn[20005];  ///存顶点访问顺序
int scc[20005];  ///缩图,存强连通块
int ind[20005];  ///缩图,存入度数
int oud[20005];  ///缩图,存出度数
bool vis[20005]; ///访问标记
int cnt, sc;
struct Node {
int u;
Node *next;
Node() {}
Node(int uu, Node *p=NULL) : u(uu), next(p){}
};
struct Graph {
Node *head;
Graph() {}
}G[20005], NG[20005];
void initG(int n) {///初始化
for (int i=1; i<=n; ++i) {
G[i].head = NULL;
NG[i].head = NULL;
}
}
void buildG(Graph g[], int v, int u) {///建图
Node *p = new Node(u);
p->next = g[v].head;
g[v].head = p;
}
void dfs_one(int k) {///第一次dfs求顶点访问顺序
vis[k] = true;
for (Node *p=G[k].head; p!=NULL; p=p->next) {
if (!vis[p->u]) {
dfs_one(p->u);
}
}
dfn[++cnt] = k;///顶点访问顺序
}
void dfs_two(int k) {///第二次dfs缩图
scc[k] = sc;     ///同一个强连通分量标记为一样的标志
vis[k] = true;
for (Node *p=NG[k].head; p!=NULL; p=p->next) {
if (!vis[p->u]) {
dfs_two(p->u);
}
}
}
int kosaraju(int n) {
cnt = 0;
memset(vis, false, sizeof(vis));
for (int i=1; i<=n; ++i) {///求顶点访问顺序
if (!vis[i]) {
dfs_one(i);
}
}
memset(vis, false, sizeof(vis));
sc = 0;
for (int i=n; i>0; --i) {///缩图
if (!vis[dfn[i]]) {
++sc;
dfs_two(dfn[i]);
}
}

for (int i=1; i<=sc; ++i) {
ind[i] = oud[i] = 0;
}
for (int i=1; i<=n; ++i) {
for (Node *p=G[i].head; p!=NULL; p=p->next) {
if (scc[i] != scc[p->u]) {///不在同一个强连通内
++oud[scc[i]];
++ind[scc[p->u]];
}
}
}
int max1 = 0;
int max2 = 0;
for (int i=1; i<=sc; ++i) {
if (ind[i] == 0) ++max1;
if (oud[i] == 0) ++max2;
}

int ans = max(max1, max2);
if (sc == 1) ans = 0;
return ans;
}
void deleteG(Graph g[], int n) {///释放图
for (int i=1; i<=n; ++i) {
Node *p = g[i].head;
if (p != NULL) {
Node *pt = p->next;
while (p != NULL) {
delete p;
if (pt == NULL) {
p = NULL;
break;
}else {
p = pt;
pt = pt->next;
}
}
g[i].head = NULL;
}
}
}
int main(void) {
int t, n, m;

scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
initG(n);
while (m--) {
int u, v;
scanf("%d%d", &v, &u);
buildG(G, v, u);
buildG(NG, u, v);
}

int ans = kosaraju(n);
printf("%d\n", ans);

deleteG(G, n);
deleteG(NG, n);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息