您的位置:首页 > 其它

LightOJ - 1429-Assassin`s Creed (II)(tarjan找环缩点+bfs+最小路径覆盖)

2017-04-22 16:59 429 查看
原题地址:点击打开链接

题意:有n个顶点组成的有向图,每个顶点都有一个目标,现在要派刺客去杀这些目标,刺客可以从任意点出发,且可以多次路过一个顶点,问至少要派

多少刺客可以杀死所有的目标。

思路:该题是一个最小路径覆盖的问题,(最小路径算法的介绍请看:点击打开链接),由于可能有环所以要用tarjian算法找出环并缩点,因为该题说可以重复

经过一个顶点,所以我们要在求最小路径覆盖之前要先预处理顶点的连通性。

在二分图里  最小路径覆盖=顶点数-最大匹配。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
#define INF 1<<31-1
using namespace std;
vector<int>g[1010];
vector<int>newG[1010];
int used[1010];
int match[1010];
int dfn[1010]; //记录初次访问该顶点的时间
int low[1010]; //记录顶点能达到的最早的时间
int instack[1010]; //标记是否在栈内
int time; //时间戳
int cnt; //强连通分量个数
int belong[1010] ; //记录顶点所属强连通分量
void bfs(int s) //预处理顶点的连通性
{
queue<int>Q;
Q.push(s);
used[s]=1;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(int i=0;i<newG[u].size();i++)
{
int v=newG[u][i];
if(!used[v])
{
used[v]=1;
newG[s].push_back(v);
Q.push(v);
}
}
}
}
stack<int>S;
void tarjan(int u)
{
dfn[u]=low[u]=++time;
S.push(u);
instack[u]=1;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(dfn[v]==-1)
{
tarjan(v);
low[u]=min(low[v],low[u]);
}
else if(instack[v]==1)
{
low[u]=min(dfn[v],low[u]);
}
}
if(dfn[u]==low[u])
{
int a;
cnt++;
do
{
a=S.top();
S.pop();
instack[a]=0;
belong[a]=cnt;
}while(a!=u);
}
}
void suodian(int n) //缩点,建新图
{
int i,j,u,v;
for(i=1;i<=n;i++)
{
u=belong[i];
for(j=0;j<g[i].size();j++)
{
v=belong[g[i][j]];
if(u!=v)
newG[u].push_back(v);
}
}
}
bool find(int u)
{
for(int i=0;i<newG[u].size();i++)
{
int v=newG[u][i];
if(!used[v])
{
used[v]=1;
if(match[v]==-1||find(match[v]))
{
match[v]=u;
return true;
}
}
}
return false;
}
int solve(int n)
{
int i,res=0;
time=0;
cnt=0;
memset(match,-1,sizeof(match));
memset(dfn,-1,sizeof(dfn));

for(i=1;i<=n;i++)
{
if(dfn[i]==-1)
{
tarjan(i);
}
}
suodian(n);
for(i=1;i<=cnt;i++)
{
memset(used,0,sizeof(used));
bfs(i); // 预处理顶点连通性
}

for(i=1;i<=cnt;i++)
{
memset(used,0,sizeof(used));
if(find(i))
{
res++;
}
}

return cnt-res;
}
int main()
{
int i,t,n,m,u,v,k=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
g[i].clear();
newG[i].clear();
}
for(i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
int res=solve(n);
printf("Case %d: %d\n",++k,res);
}
return 0;
}
/*

3
5 4
1 2
1 3
4 1
5 1

7 0

8 8
1 2
2 3
3 4
4 1
1 6
6 7
7 8
8 6
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息