tyvj间谍网络(强连通分量)题解
题目描述见下列网址
http://www.tyvj.cn/ (P1153)
经过对题目的分析,可以得出,题目的要求就是求出图中的强连通分量连,然后进行缩点操作,最后枚举每一个入度为0的点,如果可以有间谍的话,将贿赂所消耗的钱累加,否则的话记录下入度为0的点中最小点的标号。
分析:
1.对强连通分量的求解使用TARJAN算发(因为数据量太大,3000,如果直接使用DFS的话为n^2+M,可能会超时)。
2.在进行强连通分量缩点操作时,顺便记录缩成的点中贿赂间谍消耗的最小值,赋成这个点的权值。
3.对缩成的点进行入度的处理,即循环每一个强连通分量中的点,如果同一个强连通分量的点之间存在边,则子节点的入度减1。
最后枚举强连通分量中的点,只要存在一个点的入度大于0,则缩成的点的入度就大于0。
4.最后就是用模拟链表存储边时,数组不可以开的太小。(本人就因为这个原因悲剧了两次)。
5.ps.同样重要的是,C++中千万不要使用流读入,因为流的兼容性太强,速度奇慢,换标准输出(据统计标准读入是所有读入方法(readln,cin....)中
最快的)
下面附上C++代码
#include <cstdio> #include <cstdlib> #include <cstring> struct { int x,y,n; }e[100001]; int f[3001]; int o; bool v[3001]; bool vs[1001]; int b[3001][3001],dfn[3001],low[3001],zhan=0,lian,ti=0; int stick[3001],money[3001]; int into[3001]; bool map[3001][3001]; int intot[3001]; int min(int a,int b) { return (a<b)?a:b; } void add(int a,int b) { o++; e[o].x=a; e[o].y=b; e[o].n=f[a]; f[a]=o; } void tanzhan(int u) { lian++; int j=1; while (stick[zhan]!=u) { b[lian][j++]=stick[zhan--]; vs[stick[zhan]]=false; } b[lian][j++]=stick[zhan--]; vs[u]=false; b[lian][0]=j-1; } void tarjan(int u) { dfn[u]=++ti; if (low[u]==0) low[u]=dfn[u]; stick[++zhan]=u; vs[u]=true; int t=f[u]; v[u]=true; while (e[t].y!=0) { if (v[e[t].y]==false) { tarjan(e[t].y); low[u]=min(low[u],low[e[t].y]); }else { if (vs[e[t].y]==true) low[u]=min(low[u],dfn[e[t].y]); } t=e[t].n; } if (dfn[u]==low[u]) tanzhan(u); } int main() { freopen("tarjan.in","r",stdin); freopen("tarjan.out","w",stdout); int n; memset(map,0,sizeof(map)); scanf("%d",&n); int p; scanf("%d",&p); for (int i=1;i<=p;i++) { int a,b; scanf("%d%d",&a,&b); if (money[a]==0||b<money[a]) money[a]=b; } int r;scanf("%d",&r); for (int i=1;i<=r;i++) { int a,b;scanf("%d%d",&a,&b); add(a,b); into[b]++; map[a][b]=1; } for (int i=1;i<=n;i++) if (v[i]==false) tarjan(i); for (int i=1;i<=lian;i++) { intot[i]=0; for (int j=1;j<=b[i][0];j++) for (int k=1;k<=b[i][0];k++) if (k!=j) { if (map[b[i][j]][b[i][k]]==1) into[b[i][k]]--; } for (int j=1;j<=b[i][0];j++) if (into[b[i][j]]>0) intot[i]=1; } int sum=0; int cant=100000000; for (int i=1;i<=lian;i++) { if (intot[i]==0) { int maxx=100000000; for (int j=1;j<=b[i][0];j++) if (money[b[i][j]]!=0&&money[b[i][j]]<maxx) maxx=money[b[i][j]]; if (maxx==100000000) { for (int j=1;j<=b[i][0];j++) if (b[i][j]<cant) cant=b[i][j]; }else if (maxx!=100000000) sum+=maxx; } } if (cant==100000000) { printf("YES\n"); printf("%d\n",sum); } else { printf("NO\n"); printf("%d\n",cant); } return 0; }
本文地址:http://www.cnblogs.com/foreverzsz/archive/2010/11/07/1870968.html
(foreverzsz原创,转载请注明出处)