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); } }
【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; }
【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; }
【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; }
利用方向只能像东南这个条件,直接检查是是不是入度为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; }
【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年全国多校算法寒假训练营练习比赛(第四场)nowcoder
2018年全国多校算法寒假训练营练习比赛(第四场)-A石油采集(匈牙利算法)
2018年全国多校算法寒假训练营练习比赛(第四场)-D小明的挖矿之旅
牛客网NowCoder 2018年全国多校算法寒假训练营练习比赛(第四场)A.石油采集(dfs) B.道路建设(最小生成树prim) C.求交集(暴力) F.Call to your teacher