codevs 4093 EZ的间谍网络

同样是tarjan此题细节超多。。。

大体思路是tarjan缩点,然后只考虑入度为0的连通块,对能不能收买分别讨论即可。

然后第一次忘了打时间戳。

然后第二次忘了判同在一个强连通分量里的情况。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<cmath>
#define maxn 1234567
#define maxv 3005
#define maxe 8005
using namespace std;
struct edge
{
int u,v,nxt;
}e[maxe];
int n,p,r,g[maxv],nume=0,person[maxv];
int low[maxv],dfn[maxv],times=0,father[maxv];
int cnt=0,minn[maxv];
int inq[maxv],ans,regis[maxv],ccc=0,kkk[maxv];
bool vis[maxv],ins[maxv];
stack <int> s;
void addedge(int u,int v)
{
e[++nume].u=u;
e[nume].v=v;
e[nume].nxt=g[u];
g[u]=nume;
}
void tarjan(int u)
{
low[u]=dfn[u]=++times;
vis[u]=true;
s.push(u);
ins[u]=true;
for (int i=g[u];i;i=e[i].nxt)
{
int v=e[i].v;
if (vis[v]==false)
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (ins[v]==true)
low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u])
{
int vtx,cc=0;
cnt++;
do
{
vtx=s.top();
s.pop();
father[vtx]=cnt;
ins[vtx]=false;
if (person[vtx]!=0) minn[cnt]=min(minn[cnt],person[vtx]);
kkk[cnt]=min(kkk[cnt],vtx);
}
while (vtx!=u);
}
}
int main()
{
memset(vis,false,sizeof(vis));
memset(person,0,sizeof(person));
memset(ins,false,sizeof(vis));
memset(g,0,sizeof(g));
memset(inq,0,sizeof(inq));
scanf("%d%d",&n,&p);
for (int i=1;i<=n;i++)
{
minn[i]=maxn;
kkk[i]=maxn;
}
int x,y;
for (int i=1;i<=p;i++)
{
scanf("%d%d",&x,&y);
person[x]=y;
}
scanf("%d",&r);
for (int i=1;i<=r;i++)
{
scanf("%d%d",&x,&y);
addedge(x,y);
}
for (int i=1;i<=n;i++)
father[i]=i;
for (int i=1;i<=n;i++)
if (vis[i]==false)
tarjan(i);
for (int i=1;i<=nume;i++)
if (father[e[i].u]!=father[e[i].v])
inq[father[e[i].v]]++;
int sum=0,flag=0,ans=maxn;
for (int i=1;i<=cnt;i++)
{
if ((minn[i]==maxn) && (inq[i]==0))
{
flag=1;
ans=min(ans,kkk[i]);
}
else if ((minn[i]!=maxn) && (inq[i]==0)) sum=sum+minn[i];
}
if (flag==0)
printf("YES %d ",sum);
else
printf("NO %d ",ans);
return 0;
}

原文地址:https://www.cnblogs.com/ziliuziliu/p/5081166.html