POJ上面这个题的时限是15000ms,看着都DT,最起码挺吓人的……
题意是,N个男生和N个女生,告诉你每个男生喜欢的女生编号,然后给出一个初始匹配(这个初始匹配是完备匹配),然后求所有可能的完备匹配,按升序输出。当然,如果暴整的话(当然我没试过),2000个男生+2000个女生,最多有20W条有向边,会很销魂吧应该……
看了一个神牛的报告,把这个转化成强连通问题:
首先按照给出的有向边建图,然后根据最后的那个完备匹配在图中加入反向边(就是根据那个完备匹配连 女生 到 男生 的边),那么在这个图中,属于同一个强连通的点对一定是合法点对。把他们排序输出即可。
我开始用的是优先队列,自以为会很快,跑了3800+ms,换成sort,是3400ms+,下面是代码:
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
const int N = 4010;
struct node{
int val;
bool operator < (const node &a) const{
return a.val<val;
}
};
struct Edge{
int s,e,next;
}edge[300010];
int n,e_num,vis_num,cnt;
int head[N],tim[N],low[N],instack[N],belong[N];
void AddEdge(int a,int b){
edge[e_num].s=a; edge[e_num].e=b; edge[e_num].next=head[a]; head[a]=e_num++;
}
void getmap(){
int i,j,m,x;
e_num=0;
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++){
scanf("%d",&m);
for(j=1;j<=m;j++){
scanf("%d",&x);
AddEdge(i,n+x);
}
}
for(i=1;i<=n;i++)
{
scanf("%d",&x);
AddEdge(n+x,i);
}
}
stack <int> st;
void tarjan(int x){
int i,j;
tim[x]=low[x]=++vis_num;
st.push(x);
instack[x]=1;
for(i=head[x];i!=-1;i=edge[i].next){
int u=edge[i].e;
if(tim[u]==-1){
tarjan(u);
if(low[u]<low[x])low[x]=low[u];
}
else if(instack[u] && tim[u]<low[x])
low[x]=tim[u];
}
if(tim[x]==low[x]){
cnt++;
do{
j=st.top();
st.pop();
instack[j]=0;
belong[j]=cnt;
}while(j!=x);
}
}
/*用sort排序的话,会更快
void out(int A[], int len)
{
sort(A+1, A+1+len);
printf("%d ", len);
for(int j=1; j<len; ++j) printf("%d ", A[j]);
printf("%d\n", A[len]);
}
*/
void solve(){
int i,j,A[N];
cnt=vis_num=0;
memset(tim,-1,sizeof(tim));
memset(low,0,sizeof(low));
memset(instack,0,sizeof(instack));
for(i=1;i<=2*n;i++){
if(tim[i]==-1)tarjan(i);
}
node cur;
priority_queue <node> q;
for(i=1;i<=n;i++){
int num=0;
for(j=head[i];j!=-1;j=edge[j].next){
int u=edge[j].e;
if(belong[u]==belong[i]){
cur.val=u-n;
q.push(cur);
num++;
}
}
printf("%d",num);
while(!q.empty()){
node v=q.top();
q.pop();
printf(" %d",v.val);
}
puts("");
}
/*用sort的代码
for(i=1;i<=n;i++){//遍历图中的边,如果起点终点在同一个连通分量,那么就是合法点对
int num=0;
for(j=head[i];j!=-1;j=edge[j].next){
int u=edge[j].e;
if(belong[i]==belong[u]){
A[++num]=u-n;
}
}
out(A,num);
}
*/
//puts("");这里需要注意,ZOJ的输出比POJ多要求了一个空行
}
int main()
{
while(~scanf("%d",&n))
{
getmap();
solve();
}
return 0;
}