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
*/
题意:有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
*/
相关文章推荐
- lightoj 1429 - Assassin`s Creed (II) 【BFS预处理传递闭包 + SCC缩点 + DAG最小路径覆盖】
- 【LightOJ】Assassin`s Creed (II) (缩点,传递闭包,二分图匹配,最小路径覆盖)
- Light OJ 1429 Assassin`s Creed (II) BFS+缩点+最小路径覆盖
- Light OJ 1429 Assassin`s Creed (II) BFS+缩点+最小路径覆盖
- 【最小路径覆盖】Assassin`s Creed (II)
- lightoj 1429 - Assassin`s Creed (II)
- Light OJ 1406 Assassin`s Creed 状态压缩DP+强连通缩点+最小路径覆盖
- Light OJ 1406 Assassin`s Creed 状态压缩DP+强连通缩点+最小路径覆盖
- LightOJ - 1429 Assassin`s Creed (II)(二分图)
- Light OJ 1406 Assassin`s Creed 减少国家DP+支撑点甚至通缩+最小路径覆盖
- HDU 3861 The King’s Problem(tarjan缩点+最小路径覆盖ISAP)
- HDU 3991 Harry Potter and the Present II 最小路径覆盖 建图
- loj 1429(可相交的最小路径覆盖)
- The King’s Problem(tarjan求强连通分量缩点+匈牙利求有向无环图的最小路径覆盖)
- HDU 3991 Harry Potter and the Present II(Floyd+DAG最小路径覆盖)
- 强连通分量(强连通缩点(tarjan))+最小路径覆盖(匈牙利算法)
- hdoj 3861 The King’s Problem 【有向图tarjan求SCC + 缩点 + 最小路径覆盖】
- hdu 3861(tarjan 缩点 + 二分图匹配 求最小路径覆盖
- HDU 3991 Harry Potter and the Present II(Floyd+DAG最小路径覆盖)
- Light OJ 1429 - Assassin`s Creed (II)