题目描述见下列网址
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原创,转载请注明出处)