UVA11324_The Largest Clique_tarjan求强连通分量+DP求最长路

来源:互联网 发布:凯迪网络手机版 编辑:程序博客网 时间:2024/05/22 07:52

题意:

给一个图,求它的一个最大子图,使得图中任意两点之间都至少有一条路径【注意是两点之间有路径,而不是可以到达任意一点,所以不是强连通分量】

【PS:题中给的传递闭包其实是个迷惑,没有用,因为在原图上做,和在传递闭包的图上做是完全一样的】

题解:


先求出图中的所有强连通分量,将每个分量缩为一点使之成为一个DAG图,这样在这个无环图中,找子图使得满足条件

关键:

把问题“使得子图中任意两点之间都至少有一条路径”转化成“求一条最长路径”

这个非常关键,反正我刚开始挺难想的,不过推一推确实是这样,由于任意两点之间都有路,所以所有的路都可以压缩到同一条路径上,如果不能压缩,说明这个子图一定存在两点之间没有有向路径


而求最长路的问题又可以用DP来做,状态转移方程为  dp[u]=val[u]+max(dp[v])

其中,v是u的全部子节点,val[u]为当前标号为u的强连通分量中的结点个数,这样就可以求出来最大子图了



原题:

Problem B: The Largest Clique

Given a directed graph G, consider the following transformation. First, create a new graphT(G) to have the same vertex set as G. Create a directed edge betweentwo vertices u and v in T(G) if and only if there is a pathbetween u and v in G that follows the directed edges only in the forwarddirection. This graph T(G) is often called the transitive closure of G.

We define a clique in a directed graph as a set of vertices U such thatfor any two vertices u and v in U, there is a directededge either from u to v or from v to u (or both).The size of a clique is the number of vertices in the clique.

The number of cases is given on the first line of input. Each test case describes a graph G.It begins with a line of two integersn and m, where 0 ≤ n ≤ 1000 is the number ofvertices of Gand 0 ≤ m ≤ 50,000 is the number of directed edges of G.The vertices of G are numbered from 1 to n.The following m lines contain two distinct integers u and vbetween 1 and n which definea directed edge from u to v in G.

For each test case, output a single integer that is the size of the largest clique in T(G).

Sample input

15 51 22 33 14 15 2

Output for sample input

4 


代码:

RunIDUserProblemResultMemoryTimeLanguageLengthSubmit Time1321499chengtbfAAccepted0 KB152 msC++ 4.5.32550 B2013-07-21 21:58:40


#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<stack>#define N 1005#define M 50005using namespace std;int dfs_time;int dfn[N],low[N];int color[N];int dfs_color;int n,m;struct MyStruct{int u,v;}edge[M];vector<int>f[N];vector<int>DAG[N];//存缩点后的图stack<int>sta;int visited[N];int in_stack[N];//判断是否在栈中int dp[N];int val[N];//即每个强连通分量中的结点个数int vis_DAG[N];//标记DAG图中的缩点有没有被访问过int find_min(int a,int b){return a<b?a:b;}void tarjan(int u){dfn[u]=low[u]=++dfs_time;visited[u]=1;sta.push(u);in_stack[u]=1;int child;for (int i = 0; i <f[u].size() ; i++){child=f[u][i];if (!visited[child]){tarjan(child);low[u]=find_min(low[u],low[child]);}else if(in_stack[child]){low[u]=find_min(low[u],dfn[child]);}}if (dfn[u]==low[u])//说明u是当前连通分量的根{dfs_color++;do//弹出当前强连通分量{child=sta.top();color[child]=dfs_color;sta.pop();in_stack[child]=0;val[dfs_color]++;} while (u!=child);}}void dp_DAG(int u)//这里的u是强连通分量的缩点的标号{vis_DAG[u]=1;int max=0;int child;for (int i = 0; i < DAG[u].size(); i++){child=DAG[u][i];if (!vis_DAG[child]){dp_DAG(child);}if (max<dp[child]){max=dp[child];}}dp[u]=val[u]+max;//如果dfs搜到叶子结点,就直接到这一步,此时max=0,恰好满足dp[u]=val[u]}int main(){int i,j,a,b,t;int point_u,point_v;int ans;scanf("%d",&t);while (t--){scanf("%d%d",&n,&m);for ( i = 0; i <=n ; i++){f[i].clear();DAG[i].clear();}if (!sta.empty()){sta.pop();}memset(visited,0,sizeof(visited));memset(val,0,sizeof(val));memset(in_stack,0,sizeof(in_stack));memset(dp,0,sizeof(dp));memset(vis_DAG,0,sizeof(vis_DAG));dfs_time=0;dfs_color=0;ans=0;for ( i = 1; i <=m ; i++)//输入,存边,存图{scanf("%d%d",&a,&b);edge[i].u=a;edge[i].v=b;f[a].push_back(b);}for ( i = 1; i <=n ; i++)//求强连通分量{if (!visited[i]){tarjan(i);}}for ( i = 1; i <=m ; i++)//存DAG图{point_u=color[edge[i].u];point_v=color[edge[i].v];DAG[point_u].push_back(point_v);}for ( i = 1; i <=dfs_color ; i++){if (!vis_DAG[i]){dp_DAG(i);}}for ( i = 1; i <=dfs_color; i++){if (dp[i]>ans){ans=dp[i];}}printf("%d\n",ans);}return 0;}


原创粉丝点击