POJ1904(有向图缩点+输入输出挂参考)

King‘s Quest

Time Limit: 15000MS   Memory Limit: 65536K
Total Submissions: 8311   Accepted: 3017
Case Time Limit: 2000MS

Description

Once upon a time there lived a king and he had N sons. And there were N beautiful girls in the kingdom and the king knew about each of his sons which of those girls he did like. The sons of the king were young and light-headed, so it was possible for one son to like several girls.

So the king asked his wizard to find for each of his sons the girl he liked, so that he could marry her. And the king‘s wizard did it -- for each son the girl that he could marry was chosen, so that he liked this girl and, of course, each beautiful girl had to marry only one of the king‘s sons.

However, the king looked at the list and said: "I like the list you have made, but I am not completely satisfied. For each son I would like to know all the girls that he can marry. Of course, after he marries any of those girls, for each other son you must still be able to choose the girl he likes to marry."

The problem the king wanted the wizard to solve had become too hard for him. You must save wizard‘s head by solving this problem.

Input

The first line of the input contains N -- the number of king‘s sons (1 <= N <= 2000). Next N lines for each of king‘s sons contain the list of the girls he likes: first Ki -- the number of those girls, and then Ki different integer numbers, ranging from 1 to N denoting the girls. The sum of all Ki does not exceed 200000.

The last line of the case contains the original list the wizard had made -- N different integer numbers: for each son the number of the girl he would marry in compliance with this list. It is guaranteed that the list is correct, that is, each son likes the girl he must marry according to this list.

Output

Output N lines.For each king‘s son first print Li -- the number of different girls he likes and can marry so that after his marriage it is possible to marry each of the other king‘s sons. After that print Li different integer numbers denoting those girls, in ascending order.

Sample Input

4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4

Sample Output

2 1 2
2 1 2
1 3
1 4题意:一个国王有N个王子。一共有N个女孩,每个王子可以喜欢多个女孩,但只能取一个女孩。给定一个参考结婚列表,问每个王子可分别与哪几个女孩结婚。思路:王子与女孩之间建立有向图,再根据参考结婚列表建立反向边,那么与王子处于同一个连通分量的女孩且是王子喜欢的可以和王子结婚。附输入输出挂
#include"cstdio"
#include"cstring"
#include"algorithm"
using namespace std;
const int MAXN=4010;
struct Edge{
    int to,next;
}es[210000];
int V;
void Scan(int &val)
{
    char ch;
    int x=0;
    bool flag=true;
    ch=getchar();
    if(ch==‘-‘)    flag=false;
    else if(‘0‘<=ch&&ch<=‘9‘)    x=(ch-‘0‘);
    while((ch=getchar())&&‘0‘<=ch&&ch<=‘9‘)
        x=x*10+ch-‘0‘;
    val=(flag==true)?x:-x;
}
void Print(int x)
{
    if(x>9)    Print(x/10);
    putchar(x%10+‘0‘);
}
int head[MAXN],tot;
void add_edge(int u,int v)
{
    es[tot].to=v;
    es[tot].next=head[u];
    head[u]=tot++;
}
int index;
int dfn[MAXN],low[MAXN];
int stack[MAXN],top;
int cpnt[MAXN],cnt;
bool instack[MAXN];
void tarjan(int u)
{
    instack[u]=true;
    stack[top++]=u;
    dfn[u]=low[u]=++index;
    for(int i=head[u];i!=-1;i=es[i].next)
    {
        int v=es[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])    low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        int v;
        cnt++;
        do{
            v=stack[--top];
            instack[v]=false;
            cpnt[v]=cnt;
        }while(u!=v);
    }
}
int ans[MAXN];
void solve()
{
    memset(ans,0,sizeof(ans));
    for(int i=1;i<=V+V;i++)
        if(!dfn[i])    tarjan(i);

    for(int i=1;i<=V;i++)
    {
        int counter=0;
        for(int j=head[i];j!=-1;j=es[j].next)
        {
            int v=es[j].to;
            if(cpnt[v]==cpnt[i])    ans[counter++]=v-V;
        }
        sort(ans,ans+counter);
        Print(counter);
        for(int j=0;j<counter;j++)    putchar(‘ ‘),Print(ans[j]);
        putchar(‘\n‘);
    }
}

int main()
{
    while(scanf("%d",&V)!=EOF)
    {
        tot=index=top=cnt=0;
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(instack,false,sizeof(instack));
        memset(cpnt,0,sizeof(cpnt));
        for(int i=1;i<=V;i++)
        {
            int k;
            Scan(k);
            while(k--)
            {
                int v;
                Scan(v);
                add_edge(i,V+v);
            }
        }
        for(int i=1;i<=V;i++)
        {
            int v;
            Scan(v);
            add_edge(V+v,i);
        }
        solve();
    }
    return 0;
}
kosaraju算法——有向图缩点利器
#include"cstdio"
#include"cstring"
#include"algorithm"
#include"vector"
using namespace std;
const int MAXN=4010;
vector<int> G[MAXN];
vector<int> rG[MAXN];
vector<int> vs;
int V,E;
void add_edge(int u,int v)
{
    G[u].push_back(v);
    rG[v].push_back(u);
}
int vis[MAXN];
int cpnt[MAXN];
void dfs(int u)
{
    vis[u]=1;
    for(int i=0;i<G[u].size();i++)
        if(!vis[G[u][i]])    dfs(G[u][i]);
    vs.push_back(u);
}
void rdfs(int u,int k)
{
    vis[u]=1;
    cpnt[u]=k;
    for(int i=0;i<rG[u].size();i++)
        if(!vis[rG[u][i]])    rdfs(rG[u][i],k);
}
void scc()
{
    vs.clear();
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=V+V;i++)
        if(!vis[i])    dfs(i);
    memset(vis,0,sizeof(vis));
    int k=1;
    for(int i=vs.size()-1;i>=0;i--)
        if(!vis[vs[i]])    rdfs(vs[i],k++);
}
int ans[MAXN];
void solve()
{
    memset(ans,0,sizeof(ans));
    scc();
    for(int i=1;i<=V;i++)
    {
        int counter=0;
        for(int j=0;j<G[i].size();j++)
        {
            int v=G[i][j];
            if(cpnt[i]==cpnt[v])    ans[counter++]=v-V;
        }
        sort(ans,ans+counter);
        printf("%d",counter);
        for(int i=0;i<counter;i++)    printf(" %d",ans[i]);
        printf("\n");
    }
}
int main()
{
    while(scanf("%d",&V)!=EOF)
    {
        for(int i=1;i<=V+V;i++)
        {
            G[i].clear();
            rG[i].clear();
        }

        for(int i=1;i<=V;i++)
        {

            int k;
            scanf("%d",&k);
            while(k--)
            {
                int v;
                scanf("%d",&v);
                add_edge(i,v+V);
            }
        }
        for(int i=1;i<=V;i++)
        {
            int v;
            scanf("%d",&v);
            add_edge(v+V,i);
        }

        solve();
    }
    return 0;
}
				
时间: 2024-12-17 11:59:57

POJ1904(有向图缩点+输入输出挂参考)的相关文章

hdu 3072 有向图缩点成最小树形图计算最小权

题意,从0点出发,遍历所有点,遍历边时候要付出代价,在一个SCC中的边不要付费.求最小费用. 有向图缩点(无需建立新图,,n<=50000,建则超时),遍历边,若不在一个SCC中,用一个数组更新记录最小到达该连通分量的最小边权即可...边聊天,边1A,哈哈... #include<iostream> #include<stack> #include<queue> #include<cstdio> #include<cstring> usin

HDU1269(有向图缩点模板题)

迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 11070    Accepted Submission(s): 4948 Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A

hdu 3639 有向图缩点+建反向图+搜索

题意:给个有向图,每个人可以投票(可以投很多人,一次一票),但是一个人只能支持一人一次,支持可以传递,自己支持自己不算,被投支持最多的人. 开始想到缩点,然后搜索,问题是有一点想错了!以为支持按票数计算,而不是按人数!还各种树形dp/搜索求可以到达边数..提交WA了... 又反复读了题目之后才发现..错误..只要人数就行...问题简单了许多... 缩点成有向无环图后:每个SCC中支持的人数就是scc里面的人,要求可到达的点最多的点,当然要放过来求方便:反向图那个点可以到达的点最多!于是建反向图直

输入输出挂,手动扩栈。

//#pragma comment(linker, "/STACK:102400000,102400000") c++扩栈 /* int __size__ = 256 << 20;//256 兆 char * __p__ = (char *)malloc(__size__) + __size__; __asm__("movl %0,%%esp\n"::"r"(__p__)); */ /* inline bool scanf_(int

对tarjan——有向图缩点算法的理解

开始学tarjan的时候,有关无向图的割点.桥.点双边双缩点都比较容易地理解了,唯独对有向图的缩点操作不甚明了.通过对luoguP2656_采蘑菇一题的解决,大致搞清了tarjan算法的正确性. 首先放出有向图缩点tarjan函数的写法: void tarjan(int u) { dfn[u] = low[u] = ++timer; sta[++stp] = u, ins[u] = true; for (int i = head[u]; i; i = edge[i].nxt) { int v =

各种各样的输入输出挂!!!

先上C的: 适用于正int的. 1 int read() 2 { 3 char ch=' '; 4 int ans=0; 5 while(ch<'0' || ch>'9') 6 ch=getchar(); 7 while(ch<='9' && ch>='0') 8 { 9 ans=ans*10+ch-'0'; 10 ch=getchar(); 11 } 12 return ans; 13 } 适用于正负int / LL / double 的. (来自 风神) 1 t

输入输出挂

http://blog.csdn.net/shahdza/article/details/6317011 inline void in(int &ret){ char c; ret=0; while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar(); } inline bool in(int &ret){ char c; int sgn; w

并查集(集合合并) + 缩点

正解是dfs的一道题目被我以为成了并查集,结果坑了队友.现在分析一下自己当时为什么会想成是并查集呢. 题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2588 题意:告诉一张n点m边的图,求哪些边是桥.其实就是缩点,然而却大脑短路,直接认定是并查集了,现在的我真的不能理解当时的自己.... 当时想的是把所有的环缩成一个点,然后剩下的边就是桥了,缩点,第一时间想的就是并查集,其实并查集的操作一般有三个: 1.初始化 把

C语言中输入输出重定,freopen()妙用。

使用的理由(范围):如果输入数据很庞大,需要一次又一次的重新输入和调试时可采用本函数. freopen ()函数: 1.格式 FILE * freopen ( const char * filename, const char * mode, FILE * stream ); 2.参数说明 filename: 要打开的文件名 mode: 文件打开的模式,和fopen中的模式(r/w)相同 stream: 文件指针,通常使用标准流文件(stdin/stdout/stderr) 返回值:成功,则返回