您的位置:首页 > 其它

hdu 3681The King’s Problem(强连通分量+最小路径覆盖)

2015-07-26 14:12 288 查看
//hdu 3861
//给定一张有向图,划分成几个部分,
//对于u可以到v,v可以到u的点必须在同一部分
//同一部分中u可以到v或者v可以到u
//我们首先将图的强连通分量写出来,缩点,
//对于当前的有向无环图求最小路径覆盖
//为什么是最小路径覆盖呢?
//因为每一个部分的任两个点都是单向可达的,实际上每一部分都是一个路径,
//因为若分叉,由于方向性,必然分叉与分叉直接互不可达,不符合要求。
//在这里我用了比较复杂的二分图匹配算法,可以用匈牙利算法。
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=5010,M=100100;
const int INF=1<<28;
using namespace std;
struct Edge{
int from,to,next;
}edge[M],edge2[M];
int cnt,cnt2;
int head
,head2
;
void addedge(int u,int v){
edge[cnt].from=u;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
}
void addedge2(int u,int v){
edge2[cnt2].from=u;edge2[cnt2].to=v;edge2[cnt2].next=head2[u];head2[u]=cnt2++;
}

struct Tarjan{
int dfn
,low
;
int index,col;
int flag
;
int Stack
,inst
;
int top;
void init(){
index=0;col=0;top=0;
memset(dfn,0,sizeof(dfn));
memset(inst, 0, sizeof(inst));
}
void tarjan(int u){
dfn[u]=low[u]=++index;
Stack[top++]=u;inst[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(inst[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
int k;
++col;
do{
k=Stack[--top];
inst[k]=0;
flag[k]=col;
}while(k!=u);
}
}
void build(int n){
for(int i=1;i<=n;i++){
for(int j=head[i];j!=-1;j=edge[j].next){
int u=i,v=edge[j].to;
if(flag[u]!=flag[v]){
addedge2(flag[u],flag[v]);
}
}
}
}
}tj;
struct HK{//用HK算法求解二分最大匹配问题(时间复杂度为sqrt(v)*e)
int nx,ny,dis;
int cx
;//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
int cy
; //cy[i]表示右集合i顶点所匹配的左集合的顶点序号
int dx
;
int dy
;
int Q
;
bool bmask
;
bool searchpath(){//极大最短增广路集
int front,rear;
front=rear=0;//初始化队列
dis=INF;
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
for(int i=1;i<=nx;i++){
if(cx[i]==-1)//左边未匹配
{
Q[rear++]=i;//将左边节点放入队列
dx[i]=0;//分层 所有潜在起点构成0层
}
}

while(front<rear){
int u=Q[front++];//u都是未匹配的
if(dx[u]>dis)break;
for(int i=head2[u];i!=-1;i=edge2[i].next){
int v=edge2[i].to;
if(dy[v]==-1){//相邻未分层构成i+1层。
dy[v]=dx[u]+1;//v对应的距离 为u对应距离加1
if(cy[v]==-1)dis=dy[v];//右侧未匹配,更新最短距离
else{
dx[cy[v]]=dy[v]+1;//右侧已经匹配,根据已匹配边回溯。分层
Q[rear++]=cy[v];//左侧已匹配点入队。
}
}
}
}
return dis!=INF;
}
int findpath(int u){//只搜索极大最短增广路集中的边
for(int i=head2[u];i!=-1;i=edge2[i].next){
int v=edge2[i].to;
if(!bmask[v]&&dy[v]==dx[u]+1){//反向搜索的过程
bmask[v]=1;
if(cy[v]!=-1&&dy[v]==dis)continue;
if(cy[v]==-1||findpath(cy[v])){
cy[v]=u;
cx[u]=v;
return 1;
}
}
}
return 0;
}
int Max_match(){
int res=0;
memset(cx,-1,sizeof(cx));
memset(cy,-1,sizeof(cy));
while(searchpath()){
memset(bmask, 0, sizeof(bmask));
for(int i=1;i<=nx;i++){
if(cx[i]==-1){
res+=findpath(i);
}
}
}
return res;
}
};
HK hk;

void init(){
cnt=cnt2=0;
memset(head,-1,sizeof(head));
memset(head2, -1, sizeof(head2));
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
}
tj.init();
for(int i=1;i<=n;i++){
if(!tj.dfn[i])tj.tarjan(i);
}
tj.build(n);
hk.nx=hk.ny=tj.col;
printf("%d\n",tj.col-hk.Max_match());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: