第六次练习赛解题报告及标程

来源:互联网 发布:数据结构常用算法 编辑:程序博客网 时间:2024/06/14 21:58

  这次练习赛的出题其实非常有针对性,只可惜根据我的调查,体会到这一点的童鞋并不多,好伤心……

  A. Who Are My Friends?(Ⅰ)
  B. Who Are My Friends?(Ⅱ)

  两道上机原题,见上机题解。

  C. Who Are My Friends?(Ⅲ)

  我们注意到这道题与前两题的唯一区别是,查询过程是在线的,也就是说,边修改边查询。也正因为这个原因,这道题不能再用图论的方法dfs。而并查集的操作上则与之前没有什么不同。

#include<cstdio>using namespace std;const int MAXN=100005;int u[MAXN];void init(){    for(int i=0; i<MAXN; ++i)        u[i]=i;}int find(int x){    if(u[x]!=x)        u[x]=find(u[x]);    return u[x];}void merge(int x,int y){    u[find(x)]=find(y);}bool query(int x,int y){    return find(x)==find(y);}int main(){    int n,k,a,b;    while(~scanf("%d",&n))    {        init();        while(n--)        {            scanf("%d%d%d",&k,&a,&b);            switch(k)            {            case 1:                merge(a,b);                break;            case 2:                puts(query(a,b)?"Great!":"Pity...");            }        }    }}
#include<cstdio>using namespace std;const int MAXN=100005;int u[MAXN],r[MAXN];void init(){    for(int i=0; i<MAXN; ++i)    {        u[i]=i;        r[i]=0;    }}int find(int x){    if(u[x]!=x)        return find(u[x]);    return u[x];}void merge(int x,int y){    x=find(x);    y=find(y);    if(r[x]>r[y])        u[y]=x;    else    {        u[x]=y;        if(r[x]==r[y])            ++r[y];    }}bool query(int x,int y){    return find(x)==find(y);}int main(){    int n,k,a,b;    while(~scanf("%d",&n))    {        init();        while(n--)        {            scanf("%d%d%d",&k,&a,&b);            switch(k)            {            case 1:                merge(a,b);                break;            case 2:                puts(query(a,b)?"Great!":"Pity...");            }        }    }}

  D. 图的DFS遍历

  这道题有点无聊,可以说是为了卡内存而卡内存。除了动态管理内存之外,还可以用vector来模拟链表。剩下的就是链表中每个点对应的边应该排好序,然后dfs即可。我直接使用了优先队列和set两种结构来代替vector,用来维护有序性。

#include<cstdio>#include<cstring>#include<vector>#include<queue>using namespace std;const int MAXN=1005;priority_queue<int,vector<int>,greater<int> > g[MAXN];bool vis[MAXN];void dfs(int u){    vis[u]=true;    printf("%d ",u);    while(!g[u].empty())    {        int v=g[u].top();        g[u].pop();        if(!vis[v])            dfs(v);    }}int main(){    int n,k,m,u,v;    scanf("%d",&n);    while(n--)    {        scanf("%d%d",&k,&m);        while(m--)        {            scanf("%d%d",&u,&v);            g[u].push(v);            g[v].push(u);        }        memset(vis,false,sizeof(vis));        for(int i=0; i<k; ++i)            if(!vis[i])                dfs(i);        putchar('\n');    }}
#include<cstdio>#include<cstring>#include<set>using namespace std;const int MAXN=1005;set<int> g[MAXN];bool vis[MAXN];void dfs(int u){    vis[u]=true;    printf("%d ",u);    for(set<int>::iterator v=g[u].begin(); v!=g[u].end(); ++v)        if(!vis[*v])            dfs(*v);}int main(){    int n,k,m,u,v;    scanf("%d",&n);    while(n--)    {        scanf("%d%d",&k,&m);        while(m--)        {            scanf("%d%d",&u,&v);            g[u].insert(v);            g[v].insert(u);        }        memset(vis,false,sizeof(vis));        for(int i=0; i<k; ++i)            if(!vis[i])                dfs(i);        putchar('\n');        for(int i=0; i<k; ++i)            g[i].clear();    }}

  E. 蒹葭苍苍

  单点到单点的最短路,bfs即可。因为有多次询问,写floyd也是一个办法,只是因为询问次数比较少,相比前者会慢一些。我闲得蛋疼写了三份代码。

#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN=305;bool g[MAXN][MAXN],vis[MAXN];int n;int bfs(int a,int b){    queue<pair<int,int> > q;    vis[a]=true;    q.push(make_pair(a,0));    while(!q.empty())    {        pair<int,int> u=q.front();        q.pop();        for(int v=1; v<=n; ++v)            if(g[u.first][v])            {                if(v==b)                    return u.second;                if(!vis[v])                {                    vis[v]=true;                    q.push(make_pair(v,u.second+1));                }            }    }    return -1;}int main(){    int m,k,u,v;    while(~scanf("%d%d%d",&n,&m,&k))    {        memset(g,false,sizeof(g));        while(m--)        {            scanf("%d%d",&u,&v);            g[u][v]=g[v][u]=true;        }        while(k--)        {            scanf("%d%d",&u,&v);            memset(vis,false,sizeof(vis));            printf("%d\n",bfs(u,v));        }    }}
#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN=305;const int MAXM=20005;struct graph{    int head[MAXN];    int to[MAXM];    int next[MAXM];    int tot;    void init()    {        tot=0;        memset(head,0xff,sizeof(head));    }    void add(int x,int y)    {        to[tot]=y;        next[tot]=head[x];        head[x]=tot++;    }} g;bool vis[MAXN];int bfs(int a,int b){    queue<pair<int,int> > q;    vis[a]=true;    q.push(make_pair(a,0));    while(!q.empty())    {        pair<int,int> u=q.front();        q.pop();        for(int i=g.head[u.first]; ~i; i=g.next[i])        {            int v=g.to[i];            if(v==b)                return u.second;            if(!vis[v])            {                vis[v]=true;                q.push(make_pair(v,u.second+1));            }        }    }    return -1;}int main(){    int n,m,k,u,v;    while(~scanf("%d%d%d",&n,&m,&k))    {        g.init();        while(m--)        {            scanf("%d%d",&u,&v);            g.add(u,v);            g.add(v,u);        }        while(k--)        {            scanf("%d%d",&u,&v);            memset(vis,false,sizeof(vis));            printf("%d\n",bfs(u,v));        }    }}
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int MAXN=305;const int INF=0x3f3f3f3f;int g[MAXN][MAXN],n,m,k,u,v;void floyd(){    for(int k=1; k<=n; ++k)        for(int i=1; i<=n; ++i)            for(int j=1; j<=n; ++j)                g[i][j]=min(g[i][j],g[i][k]+g[k][j]);}int main(){    while(~scanf("%d%d%d",&n,&m,&k))    {        memset(g,0x3f,sizeof(g));        while(m--)        {            scanf("%d%d",&u,&v);            g[u][v]=g[v][u]=1;        }        floyd();        while(k--)        {            scanf("%d%d",&u,&v);            printf("%d\n",g[u][v]==INF?-1:g[u][v]-1);        }    }}

  F. 图的环路(Ⅰ)

  图的判环是一个很基础的问题,有很多种方式。对于无向图,我这里给出两种方法。

  一种是遍历,在遍历过程中如果遇到已经访问过的节点则意味着有环。至于使用dfs还是bfs并不重要。

#include<cstdio>#include<cstring>using namespace std;const int MAXN=10005;const int MAXM=200005;struct graph{    int head[MAXN];    int to[MAXM];    int next[MAXM];    int tot;    void init()    {        tot=0;        memset(head,0xff,sizeof(head));    }    void add(int x,int y)    {        to[tot]=y;        next[tot]=head[x];        head[x]=tot++;    }} g;bool vis[MAXN];bool dfs(int u,int c){    vis[c]=true;    bool flag=false;    for(int i=g.head[c]; !flag&&~i; i=g.next[i])    {        int v=g.to[i];        if(v!=u)        {            if(vis[v])                return true;            flag=dfs(c,v);        }    }    return flag;}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        g.init();        while(m--)        {            scanf("%d%d",&u,&v);            g.add(u,v);            g.add(v,u);        }        memset(vis,false,sizeof(vis));        bool flag=false;        for(int i=1; !flag&&i<=n; ++i)            if(!vis[i])                flag=dfs(0,i);        puts(flag?"Yes":"No");    }}
#include<cstdio>#include<cstring>#include<vector>using namespace std;const int MAXN=10005;vector<int> g[MAXN];bool vis[MAXN];bool dfs(int u,int c){    vis[c]=true;    bool flag=false;    for(int i=0; !flag&&i<g[c].size(); ++i)    {        int v=g[c][i];        if(v!=u)        {            if(vis[v])                return true;            flag=dfs(c,v);        }    }    return flag;}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        while(m--)        {            scanf("%d%d",&u,&v);            g[u].push_back(v);            g[v].push_back(u);        }        memset(vis,false,sizeof(vis));        bool flag=false;        for(int i=1; !flag&&i<=n; ++i)            if(!vis[i])                flag=dfs(0,i);        puts(flag?"Yes":"No");        for(int i=1; i<=n; ++i)            g[i].clear();    }}

  一种是并查集,不断合并有边相连的两个点,如果合并前两个点已经在一个集合内,则意味着他们在一个环里。

#include<cstdio>#include<cstring>using namespace std;const int MAXN=10005;int f[MAXN];void init(){    for(int i=0; i<MAXN; ++i)        f[i]=i;}int find(int x){    if(f[x]!=x)        f[x]=find(f[x]);    return f[x];}void merge(int x,int y){    f[find(x)]=find(y);}bool query(int x,int y){    return find(x)==find(y);}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        bool flag=false;        init();        while(m--)        {            scanf("%d%d",&u,&v);            if(query(u,v))                flag=true;            merge(u,v);        }        puts(flag?"Yes":"No");    }}

  G. 图的环路(Ⅱ)

  对于有向图,这里也给出两种做法。一种仍然是遍历,由于遍历到的已访问过的点不一定意味着成环,只有当这个点是这次遍历的祖先节点时才成环,所以记录状态时要多一个状态:0表示尚未访问,-1表示正在访问子节点,1表示已访问过所有子节点。

#include<cstdio>#include<cstring>using namespace std;const int MAXN=10005;const int MAXM=200005;struct graph{    int head[MAXN];    int to[MAXM];    int next[MAXM];    int tot;    void init()    {        tot=0;        memset(head,0xff,sizeof(head));    }    void add(int x,int y)    {        to[tot]=y;        next[tot]=head[x];        head[x]=tot++;    }} g;int vis[MAXN];bool dfs(int u){    vis[u]=-1;    bool flag=false;    for(int i=g.head[u]; !flag&&~i; i=g.next[i])    {        int v=g.to[i];        if(!~vis[v])            return true;        if(!vis[v])            flag=dfs(v);    }    vis[u]=1;    return flag;}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        g.init();        while(m--)        {            scanf("%d%d",&u,&v);            g.add(u,v);        }        memset(vis,0,sizeof(vis));        bool flag=false;        for(int i=1; !flag&&i<=n; ++i)            if(!vis[i])                flag=dfs(i);        puts(flag?"Yes":"No");    }}
#include<cstdio>#include<cstring>#include<vector>using namespace std;const int MAXN=10005;vector<int> g[MAXN];int vis[MAXN];bool dfs(int u){    vis[u]=-1;    bool flag=false;    for(int i=0; !flag&&i<g[u].size(); ++i)    {        int v=g[u][i];        if(!~vis[v])            return true;        if(!vis[v])            flag=dfs(v);    }    vis[u]=1;    return flag;}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        while(m--)        {            scanf("%d%d",&u,&v);            g[u].push_back(v);        }        memset(vis,0,sizeof(vis));        bool flag=false;        for(int i=1; !flag&&i<=n; ++i)            if(!vis[i])                flag=dfs(i);        puts(flag?"Yes":"No");        for(int i=1; i<=n; ++i)            g[i].clear();    }}

  另外拓扑排序可以判环。

#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN=10005;const int MAXM=200005;struct graph{    int head[MAXN];    int to[MAXM];    int next[MAXM];    int tot;    void init()    {        tot=0;        memset(head,0xff,sizeof(head));    }    void add(int x,int y)    {        to[tot]=y;        next[tot]=head[x];        head[x]=tot++;    }} g;int du[MAXN],n;bool toposort(){    memset(du,0,sizeof(du));    for(int i=1; i<=n; ++i)        for(int j=g.head[i]; ~j; j=g.next[j])            ++du[g.to[j]];    int tot=0;    queue<int> q;    for(int i=1; i<=n; ++i)        if(!du[i])            q.push(i);    while(!q.empty())    {        int u=q.front();        q.pop();        ++tot;        for(int i=g.head[u]; ~i; i=g.next[i])        {            int v=g.to[i];            if(!(--du[v]))                q.push(v);        }    }    return tot==n;}int main(){    int m,u,v;    while(~scanf("%d%d",&n,&m))    {        g.init();        while(m--)        {            scanf("%d%d",&u,&v);            g.add(u,v);        }        puts(!toposort()?"Yes":"No");    }}
#include<cstdio>#include<cstring>#include<vector>#include<queue>using namespace std;const int MAXN=10005;vector<int> g[MAXN];int du[MAXN],n;bool toposort(){    memset(du,0,sizeof(du));    for(int i=1; i<=n; ++i)        for(int j=0; j<g[i].size(); ++j)            ++du[g[i][j]];    int tot=0;    queue<int> q;    for(int i=1; i<=n; ++i)        if(!du[i])            q.push(i);    while(!q.empty())    {        int u=q.front();        q.pop();        ++tot;        for(int i=0; i<g[u].size(); ++i)        {            int v=g[u][i];            if(!(--du[v]))                q.push(v);        }    }    return tot==n;}int main(){    int m,u,v;    while(~scanf("%d%d",&n,&m))    {        while(m--)        {            scanf("%d%d",&u,&v);            g[u].push_back(v);        }        puts(!toposort()?"Yes":"No");        for(int i=1; i<=n; ++i)            g[i].clear();    }}

  H. Draught

  有童鞋过掉这道题还是挺开心的……这道题是CodeChef上的一道原题,算是一个比较弱的树形dp,或者一个比较难的遍历题。第一个问很好做,对于每个连通子图记录节点个数即可;第二个问,节点有气流穿过无非是两种情况,祖先至少有一个节点开窗且子树至少有一个节点开窗,或者子树有至少两个节点开窗,其中子树包括这个节点。两个问可以两次遍历分开求,也可以一次遍历一起做,两种写法稍有不同,代码都会放上来。注意在适当的地方用long long。

#include<cstdio>#include<cstring>using namespace std;const int MAXN=50005;const int MAXM=100005;struct graph{    int head[MAXN];    int to[MAXM];    int next[MAXM];    int tot;    void init()    {        tot=0;        memset(head,0xff,sizeof(head));    }    void add(int x,int y)    {        to[tot]=y;        next[tot]=head[x];        head[x]=tot++;    }} g;bool win[MAXN],air[MAXN];int vis[MAXN],wcnt;void dfs1(int u){    vis[u]=1;    if(win[u])        ++wcnt;    for(int i=g.head[u]; ~i; i=g.next[i])    {        int v=g.to[i];        if(vis[v]==0)            dfs1(v);    }}int dfs2(int u){    int ret=0,cnt=0;    vis[u]=2;    if(win[u])        ++ret;    for(int i=g.head[u]; ~i; i=g.next[i])    {        int v=g.to[i];        if(vis[v]==1)        {            int tmp=dfs2(v);            if(tmp>0)                ++cnt;            ret+=tmp;        }    }    if(cnt>=2||(cnt>=1&&win[u])||(ret>0&&wcnt>ret))        air[u]=true;    return ret;}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        for(int i=1; i<=n; ++i)            scanf("%d",&win[i]);        g.init();        while(m--)        {            scanf("%d%d",&u,&v);            g.add(u,v);            g.add(v,u);        }        memset(vis,0,sizeof(vis));        memset(air,false,sizeof(air));        int fans=0,rans=0;        for(int i=1; i<=n; ++i)        {            if(vis[i]==0)            {                wcnt=0;                dfs1(i);                fans+=(long long)wcnt*(wcnt-1)/2;                dfs2(i);            }            if(air[i])                ++rans;        }        printf("%d %d\n",fans,rans);    }}
#include<cstdio>#include<cstring>#include<vector>using namespace std;const int MAXN=50005;vector<int> E[MAXN];bool win[MAXN],air[MAXN];int vis[MAXN],wcnt;void dfs1(int u){    vis[u]=1;    if(win[u])        ++wcnt;    for(int v=0; v<E[u].size(); ++v)        if(vis[E[u][v]]==0)            dfs1(E[u][v]);}int dfs2(int u){    int ret=0,cnt=0;    vis[u]=2;    if(win[u])        ++ret;    for(int v=0; v<E[u].size(); ++v)        if(vis[E[u][v]]==1)        {            int tmp=dfs2(E[u][v]);            if(tmp>0)                ++cnt;            ret+=tmp;        }    if(cnt>=2||(cnt>=1&&win[u])||(ret>0&&wcnt>ret))        air[u]=true;    return ret;}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        for(int i=1; i<=n; ++i)            scanf("%d",&win[i]);        while(m--)        {            scanf("%d%d",&u,&v);            E[u].push_back(v);            E[v].push_back(u);        }        memset(vis,0,sizeof(vis));        memset(air,false,sizeof(air));        int fans=0,rans=0;        for(int i=1; i<=n; ++i)        {            if(vis[i]==0)            {                wcnt=0;                dfs1(i);                fans+=(long long)wcnt*(wcnt-1)/2;                dfs2(i);            }            if(air[i])                ++rans;        }        printf("%d %d\n",fans,rans);        for(int i=1; i<=n; ++i)            E[i].clear();    }}
#include<cstdio>#include<cstring>using namespace std;const int MAXN=50005;const int MAXM=100005;struct graph{    int head[MAXN];    int to[MAXM];    int next[MAXM];    int tot;    void init()    {        tot=0;        memset(head,0xff,sizeof(head));    }    void add(int x,int y)    {        to[tot]=y;        next[tot]=head[x];        head[x]=tot++;    }} g;bool win[MAXN],vis[MAXN],air[MAXN];int src;int dfs(int u){    int ret=0;    vis[u]=true;    for(int i=g.head[u]; ~i; i=g.next[i])    {        int v=g.to[i];        if(!vis[v])            ret+=dfs(v);    }    if(ret>0||(u!=src&&win[u]))        air[u]=true;    return ret+win[u];}int main(){    int n,m,u,v;    while(~scanf("%d%d",&n,&m))    {        for(int i=1; i<=n; ++i)            scanf("%d",&win[i]);        g.init();        while(m--)        {            scanf("%d%d",&u,&v);            g.add(u,v);            g.add(v,u);        }        memset(vis,false,sizeof(vis));        memset(air,false,sizeof(air));        int fans=0,rans=0;        for(int i=1; i<=n; ++i)            if(!vis[i]&&win[i])            {                src=i;                long long wcnt=dfs(i);                fans+=wcnt*(wcnt-1)/2;            }        for(int i=1; i<=n; ++i)            if(air[i])                ++rans;        printf("%d %d\n",fans,rans);    }}

  总得来说,这次练习赛重点比较了图论和并查集的相似点和不同点,练习了图论的遍历,加深对图论概念的理解(无向图、有向图、环等),也有一定挑战性的题。至于从中能得到多少收获,全看童鞋们自己了。

0 0
原创粉丝点击