2018年全国多校算法寒假训练营练习比赛(第四场)

Posted ---学习ing---

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018年全国多校算法寒假训练营练习比赛(第四场)相关的知识,希望对你有一定的参考价值。

上一场自己状态爆表;这一场队友爆表,自己捡表了;题目呢,总的来说不难,相比于前面几场比赛来说,关键在于如何建图。

像D题,在我的上一个博客里面就是一道同理的题,只需要求入度为0和出度为0的点最大数,然后特判已经强连通(比如只有一个点)的情况。

 可以看看我上一个博客,可能会对此比赛有帮助。

【A 石油采集】

   题解:二分匹配之匈牙利。

【B 道路建设】

   题解:并查集。

【C 求交集】

    题解:双指针扫一遍即可。

【D 小明的挖矿之旅】  

    题解:求度为0的点数量。

【E 通知小弟】

     题解:处理入度为0的新点;

【F Call to your teacher】

    题解:水

【G 老子的意大利炮呢】

    题解:分层+优先队列SPFA。

【H 老子的全排列呢】

    题解:STL之permutation或者试一试康拓展开。

 

 

-------------------------------代码-----------------------------

 

【A】  

12月份刚好看过队友做过此题,当然那个题我还懵b了,所以记得。

可以先去看看POJ 2446 ,POJ3020。附上队友博客:LZH

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
    int xx=0,ff=1;char ch=getchar();
    while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')ff=-1;ch=getchar();}
    while(ch>=\'0\'&&ch<=\'9\'){xx=(xx<<3)+(xx<<1)+ch-\'0\';ch=getchar();}
    return xx*ff;
}
const int ws_[4]={0,1,0,-1},ad_[4]={1,0,-1,0};
int N,cas=0;
char mp[55][55];
inline int id(int x,int y)
{return (x-1)*N+y;}
int lin[3100],len;
struct edge{
    int y,next;
}e[100010];
inline void insert(int xx,int yy){
    e[++len].next=lin[xx];
    lin[xx]=len;
    e[len].y=yy;
}
inline void ins(int xx,int yy)
{insert(xx,yy),insert(yy,xx);}
bool vis[3100];
int match[3100];
bool hun(int x){
    for(int i=lin[x];i;i=e[i].next)
        if(!vis[e[i].y]){
            vis[e[i].y]=1;
            if(match[e[i].y]==0||hun(match[e[i].y])){
                match[e[i].y]=x;
                match[x]=e[i].y;
                return 1;
            }
        }
    return 0;
}
int main(){
    //freopen("in","r",stdin);
    for(int T=read();T;T--){
        printf("Case %d: ",++cas);
        char ch;
        N=read();
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++){
                ch=getchar();
                while(ch==\' \'||ch==\'\\n\')
                    ch=getchar();
                mp[i][j]=ch;
            }
        memset(lin,0,sizeof(lin));len=0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                if(mp[i][j]==\'#\')
                    for(int k=0;k<=3;k++){
                        int tx=i+ws_[k],ty=j+ad_[k];
                        if(tx<=0||ty<=0||tx>N||ty>N)
                            continue;
                        if(mp[tx][ty]==\'#\')
                            ins(id(i,j),id(tx,ty));
                    }
        memset(match,0,sizeof(match));
        int ans=0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                if(mp[i][j]==\'#\'){
                    if(!match[id(i,j)]){
                        memset(vis,0,sizeof(vis));
                        if(hun(id(i,j)))
                            ans++;
                    }
                }
        printf("%d\\n",ans);
    }
}
View Code

【B】

 并查集

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
    int xx=0,ff=1;char ch=getchar();
    while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')ff=-1;ch=getchar();}
    while(ch>=\'0\'&&ch<=\'9\'){xx=(xx<<3)+(xx<<1)+ch-\'0\';ch=getchar();}
    return xx*ff;
}
int C,N,M;
struct edge{
    int x,y,v;
    bool friend operator<(const edge&A,const edge&B)
    {return A.v<B.v;}
}e[10010];
int father[110];
int getfather(int x){
    if(father[x]==x)
        return x;
    return father[x]=getfather(father[x]);
}
int main(){
    //freopen("in","r",stdin);
    while(scanf("%d",&C)!=EOF){
        N=read(),M=read();
        for(int i=1;i<=N;i++)
            e[i].x=read(),e[i].y=read(),e[i].v=read();
        sort(e+1,e+1+N);
        for(int i=1;i<=M;i++)
            father[i]=i;
        int cnt=0;
        long long ans=0;
        for(int i=1;i<=N;i++){
            int f1=getfather(e[i].x),f2=getfather(e[i].y);
            if(f1==f2)
                continue;
            cnt++;
            ans+=e[i].v;
            father[f2]=f1;
            if(cnt==M-1)
                break;
        }
        if(ans<=C)
            printf("Yes\\n");
        else
            printf("No\\n");
    }
    return 0;
}
View Code

【C】

由于已经有序,而且集合具有互异性,可以用双指针。

假设两个队伍,每次比较队首,小的一方后移队首。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
    int xx=0,ff=1;char ch=getchar();
    while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')ff=-1;ch=getchar();}
    while(ch>=\'0\'&&ch<=\'9\'){xx=(xx<<3)+(xx<<1)+ch-\'0\';ch=getchar();}
    return xx*ff;
}
const int maxn=1000010;
int a[maxn],b[maxn],N,M;
int ans[maxn],tp;
int main(){
    while(scanf("%d %d",&N,&M)!=EOF){
        for(int i=1;i<=N;i++)
            a[i]=read();
        for(int i=1;i<=M;i++)
            b[i]=read();
        int i=1,j=1,tp=0;
        while(i<=N&&j<=M){
            if(a[i]==b[j])
                ans[++tp]=a[i],i++,j++;
            else if(a[i]>b[j])
                j++;
            else
                i++;
        }
        if(tp==0) printf("empty\\n");
        else {
        for(int k=1;k<tp;k++)
            printf("%d ",ans[k]);
        printf("%d\\n",ans[tp]);
        }
    }
    return 0;
}
View Code

【D】

就是问有向图至少加几条有向边使得图成为强连通,结论是:当已经强连通时,答案为0(此情况就是所谓的特判);否则,答案=max(出度为0数,入度为0数)。

对于上面的结论,我以前证明过。但是找了下没找到对应的博客。

Tarjan缩点建图,求度为0的点数。我的代码如下,但可能数据有点大,只通过了87%。

//有向图缩点 ,注意scc_cnt=1时。
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000010;
int Laxt[maxn],Next[maxn<<1],To[maxn<<1],cnt,n,m;
int dfn[maxn],low[maxn],times,scc_cnt,scc[maxn];
int instc[maxn],stc[maxn],top,ans1,ans2;
int ind[maxn],oud[maxn];
char chr[1010][1010];
void update()
{
    cnt=times=scc_cnt=top=ans1=ans2=0;
    memset(Laxt,0,sizeof(Laxt));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(scc,0,sizeof(scc));
    memset(instc,0,sizeof(instc));
    memset(stc,0,sizeof(stc));
    memset(ind,0,sizeof(ind));
    memset(oud,0,sizeof(oud));
}
void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt;
    To[cnt]=v;
}
void dfs(int u)
{
    dfn[u]=low[u]=++times;
    stc[++top]=u; instc[u]=1;
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i];
        if(!dfn[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instc[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        scc_cnt++;
        while(true){
            int x=stc[top--];
            scc[x]=scc_cnt;
            instc[x]=0;
            if(x==u) break;
        }
    }
}
void tarjan()
{
   for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++){
            if(chr[i][j]==\'.\'&&!dfn[(i-1)*m+j]) dfs((i-1)*m+j);
       }
    for(int i=1;i<=n*m;i++)
      for(int j=Laxt[i];j;j=Next[j]){
        if(scc[i]!=scc[To[j]]) {
            ind[scc[To[j]]]++;
            oud[scc[i]]++;
        }
    }
         
    for(int i=1;i<=scc_cnt;i++){
        if(ind[i]==0) ans1++;
        if(oud[i]==0) ans2++;
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        update();
        for(int i=1;i<=n;i++) scanf("%s",chr[i]+1);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++){
            if(chr[i][j]==\'.\'){
                if(j+1<=m&&chr[i][j+1]==\'.\') add((i-1)*m+j,(i-1)*m+j+1);
                if(i+1<=n&&chr[i+1][j]==\'.\') add((i-1)*m+j,i*m+j);
            }
         }
        tarjan();
        if(scc_cnt==1) printf("0\\n");
        else printf("%d\\n",max(ans1,ans2));
    } return 0;
}
View Code

利用方向只能像东南这个条件,直接检查是是不是入度为0的点,或者出度为0的点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
    int xx=0,ff=1;char ch=getchar();
    while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')ff=-1;ch=getchar();}
    while(ch>=\'0\'&&ch<=\'9\'){xx=(xx<<3)+(xx<<1)+ch-\'0\';ch=getchar();}
    return xx*ff;
}
const int ws_[4]={0,1,0,-1},ad_[4]={1,0,-1,0};
int N,M;
char mp[1010][1010];
bool vis[1010][1010];
int tp1,tp2,cnt;
bool check(int xx,int yy){
    if(xx<=0||yy<=0||xx>N||yy>M)
        return 0;
    return 1;
}
void dfs(int xx,int yy){
    vis[xx][yy]=1;
    if((!check(xx+ws_[2],yy+ad_[2]))||mp[xx+ws_[2]][yy+ad_[2]]==\'#\')
        if((!check(xx+ws_[3],yy+ad_[3]))||mp[xx+ws_[3]][yy+ad_[3]]==\'#\')
            tp1++;
    if((!check(xx+ws_[0],yy+ad_[0]))||mp[xx+ws_[0]][yy+ad_[0]]==\'#\')
        if((!check(xx+ws_[1],yy+ad_[1]))||mp[xx+ws_[1]][yy+ad_[1]]==\'#\')
            tp2++;
    int tx,ty;
    for(int k=0;k<=3;k++){
        tx=xx+ws_[k],ty=yy+ad_[k];
        if(!check(tx,ty))
            continue;
        if((!vis[tx][ty])&&mp[tx][ty]==\'.\')
            dfs(tx,ty);
    }
}
int main(){
    //freopen("in","r",stdin);
    while(scanf("%d %d",&N,&M)!=EOF){
        cnt=0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=M;j++){
                char ch=getchar();
                while(ch==\' \'||ch==\'\\n\')
                    ch=getchar();
                mp[i][j]=ch;
                if(mp[i][j]==\'.\')
                    cnt++;
            }
        int ans,ans1=0,ans2=0;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=N;i++)
            for(int j=1;j<=M;j++)
                if(mp[i][j]==\'.\'&&(!vis[i][j])){
                    tp1=tp2=0;
                    dfs(i,j);
                    ans1+=tp1,ans2+=tp2;
                }
        ans=max(ans1,ans2);
        if(cnt==1)
            ans=0;
        printf("%d\\n",ans);
    }
    return 0;
}
View Code

【E】

显然,缩点,重新建图,只有入度为0的点(缩点之后的)是需要考虑的,如果入度为0的点(缩点之后,其实是个集合)里面如果没有可以通知到的,那么“-1”;

如果入度为0的点大于m,那么“-1”;    

现在看来,只需求入度为0的有哪些,然后验证每个点(集合)是否存在可以通知的人即可,而不需重新构图。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
int Laxt[maxn],Next[maxn*250],To[maxn*250],cnt,n,m;
int dfn[maxn],low[maxn],times,scc_cnt,scc[maxn];
int instc[maxn],stc[maxn],top;
int ind[maxn],ans,p[maxn],ok[maxn];
void update()
{
    cnt=times=scc_cnt=top=ans=0;
    memset(Laxt,0,sizeof(Laxt));
    memset(dfn,0,sizeof(dfn));
    memset(scc,0,sizeof(scc));
    memset(instc,0,sizeof(instc));
    memset(stc,0,sizeof(stc));
    memset(ind,0,sizeof(ind));
    memset(p,0,sizeof(p));
    memset(ok,0,sizeof(ok));
}
void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt;
    To[cnt]=v;
}
void dfs(int u)
{
    dfn[u]=low[u]=++times;
    stc[++top]=u; instc[u]=1;
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i];
        if(!dfn[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instc[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        scc_cnt++;
        while(true){
            int x=stc[top--];
            scc[x]=scc_cnt;
            instc[x]=0;
            if(x==u) break;
        }
    }
}
void tarjan()
{
    for(int i=1;i<=n;i++)
        if(!dfn[i]) dfs(i);
    for(int i=1;i<=n;i++)
      for(int j=Laxt[i];j;j=Next[j]){
        if(scc[i]!=scc[To[j]]) {
            ind[scc[To[j]]]++;
        }
    }
         
    for(int i=1;i<=scc_cnt;i++){
        if(ind[i]==0) ans++;
    }
    if(ans>m){
       printf("-1\\n");
       return ;
    }
    for(int i=1;i<=n;i++){
        if(ind[scc[i]]==0&&p[i]==1) ok[scc[i]]=1;
    }
    for(int i=1;i<=scc_cnt;i++)
     if(ind[i]==0&&!ok[i]) {
            printf("-1\\n");
            return ;
     }
    printf("%d\\n",ans);
    return ;
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        update(); int2018年全国多校算法寒假训练营练习比赛(第四场)

牛客网 2018年全国多校算法寒假训练营练习比赛(第四场)

2018年全国多校算法寒假训练营练习比赛(第四场)nowcoder

2018年全国多校算法寒假训练营练习比赛(第四场)-A石油采集(匈牙利算法)

2018年全国多校算法寒假训练营练习比赛(第四场)-D小明的挖矿之旅

牛客网NowCoder 2018年全国多校算法寒假训练营练习比赛(第四场)A.石油采集(dfs) B.道路建设(最小生成树prim) C.求交集(暴力) F.Call to your teacher