Gym -102007 :Benelux Algorithm Programming Contest (BAPC 18) (寒假自训第5场)
Posted hua-dong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gym -102007 :Benelux Algorithm Programming Contest (BAPC 18) (寒假自训第5场)相关的知识,希望对你有一定的参考价值。
A .A Prize No One Can Win
题意:给定N,S,你要从N个数中选最多是数,使得任意两个之和不大于S。
思路:排序,然后贪心的选即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; ll a[maxn]; int main() { int N,ans; ll M; scanf("%d%lld",&N,&M); rep(i,1,N) scanf("%lld",&a[i]); sort(a+1,a+N+1); ans=1; rep(i,2,N){ if(a[i]+a[i-1]<=M) ans=i; } printf("%d ",ans); return 0; }
B .Birthday Boy
题意:给出N个人的生日,让你选择一天,使得这一天的前一个生日距离它最远,如果有多个一样的,有点选择10月27之后的
思路:模拟即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; int Y[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; int vis[1000],tot; int mp[13][32],C[13][32],A[1000],B[1000]; int main() { int N,y,d,ansx=1,ansy=1,ans=-1;char c[20]; scanf("%d",&N); rep(i,1,N){ scanf("%s%d-%d",c+1,&y,&d); mp[y][d]=1; } rep(i,1,12) rep(j,1,Y[i]){ tot++; A[tot]=i; B[tot]=j; C[i][j]=tot; if(mp[i][j]) vis[tot]=1; } rep(i,1,tot) vis[i+tot]=vis[i],A[i+tot]=A[i],B[i+tot]=B[i]; rep(i,tot,tot+tot-1) { if(vis[i]) continue; int j=i; while(j-1>=i-tot+1&&!vis[j-1]) j--; if(i-j>ans){ ans=i-j,ansx=A[i],ansy=B[i]; } else if(i-j==ans&&C[ansx][ansy]<=C[10][27]&&C[A[i]][B[i]]>C[10][27]) ansx=A[i],ansy=B[i]; } printf("%02d-%02d ",ansx,ansy); return 0; }
C .Cardboard Container
题意: 给定N个1*1*1的盒子,让你用最小的表面积把它包起来.
思路: 枚举长和宽和高(因子)即可.
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=998244353; const int maxn=100+10; const int inf=1000000000; int main() { int v; cin>>v; ll ans=inf; for(int i=1;i<=v;i++){ if(v%i==0){ int l=v/i; for(int j=1;j<=l;j++){ if(l%j==0){ int k=l/j; ans=min(ans,2LL*(i*j+k*j+k*i)); } } } } cout<<ans<<endl; return 0; }
G .Game Night
模拟几种排列即可
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; int sum[maxn][3],N; char c[maxn]; int get(int A,int B,int C) { int res=0; rep(i,1,N){ int X=sum[i+sum[N][A]-1][A]-sum[i-1][A]; int Y=sum[i+sum[N][A]+sum[N][B]-1][B]-sum[i+sum[N][A]-1][B]; int Z=sum[i+N-1][C]-sum[i+sum[N][A]+sum[N][B]-1][C]; res=max(res,X+Y+Z); //cout<<A<<" "<<B<<" "<<C<<" : "<<i<<" "<<res<<endl; } return N-res; } int main() { scanf("%d%s",&N,c+1); int ans=N; rep(i,1,N) c[N+i]=c[i]; rep(i,1,N+N){ rep(j,0,2) sum[i][j]=sum[i-1][j]; if(c[i]==‘A‘) sum[i][0]++; if(c[i]==‘B‘) sum[i][1]++; if(c[i]==‘C‘) sum[i][2]++; } rep(i,0,2) rep(j,0,2) rep(k,0,2) if(i!=j&&i!=k&&j!=k) ans=min(ans,get(i,j,k)); printf("%d ",ans); return 0; }
I. In Case of an Invasion, Please...
题意:给定N城市每个城市人数是Pi,M双向路,其中S个点(S<=10)是避难所,避难所容量是Ci,(N<1e5; Ci,Pi<1e9)最小化最大的转移时间,使得所有人都转移到避难所。
思路:二分+最大流。由于图比较大,此题需要一些优化才能过。
由于上限是1e14; 所以二分次数是50次; 我们可以把距离离散化上线变为了1e6,二分次数是20;这是一个优化。
第二个优化,对于二分到的mid,如果一个城市可以去的避难所容量之和<P[i],那么显然不用跑网络流也知道不合法。
还有就是sap跑二分图比较慢,要用dinic。
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1e5+150; ll inf=1ll<<60; struct Edge { int from,to; ll cap,flow; }; struct Dinic { int n,m,s,t; vector<Edge>edges; vector<int>G[maxn]; bool vis[maxn]; int d[maxn],cur[maxn]; void init(int n) { this->n=n; for(int i=0;i<n;i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,ll cap) { edges.push_back((Edge){from,to,cap,0}); edges.push_back((Edge){to,from,0,0}); m=edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool bfs() { memset(vis,0,sizeof(vis)); queue<int>Q; Q.push(s); d[s]=0; vis[s]=1; while(!Q.empty()) { int x=Q.front();Q.pop(); for(int i=0;i<G[x].size();i++) { Edge& e=edges[G[x][i]]; if(!vis[e.to]&&e.cap>e.flow) { vis[e.to]=1; d[e.to]=d[x]+1; Q.push(e.to); } } } return vis[t]; } ll dfs(int x,ll a) { if(x==t||a==0)return a; ll flow=0,f; for(int& i=cur[x];i<G[x].size();i++) { Edge& e=edges[G[x][i]]; if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0) { e.flow+=f; edges[G[x][i]^1].flow-=f; flow+=f; a-=f; if(a==0)break; } } return flow; } ll Maxflow(int s,int t) { this->s=s,this->t=t; ll flow=0; while(bfs()) { memset(cur,0,sizeof(cur)); flow+=dfs(s,inf); } return flow; } }solve; ll d[15][maxn],sum,vis[maxn]; vector<int>G[maxn],dis[maxn]; struct node { int u; ll w; node(int a,ll b) { u=a,w=b; } bool operator<(const node&t)const { return w>t.w; } }; priority_queue<node>q; void dij(int k,int s,int n) { for(int i=1;i<=n;i++)d[k][i]=inf,vis[i]=0; d[k][s]=0; q.push(node(s,0)); while(!q.empty()) { node e=q.top();q.pop(); int u=e.u; vis[u]=0; for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(d[k][v]>d[k][u]+dis[u][i]) { d[k][v]=d[k][u]+dis[u][i]; if(!vis[v]) vis[v]=1,q.push(node(v,d[k][v])); } } } } int p[maxn],s[maxn],c[maxn],tot; ll B[maxn*20],fcy[maxn]; int main() { int n,m,k,u,v,w; scanf("%d%d%d",&n,&m,&k); int S=0,T=n+k+1; for(int i=1;i<=n;i++) { scanf("%d",&p[i]); sum+=p[i]; } for(int i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); G[u].push_back(v); dis[u].push_back(w); G[v].push_back(u); dis[v].push_back(w); } for(int i=1;i<=k;i++) { scanf("%d%d",&s[i],&c[i]); dij(i,s[i],n); } for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) B[++tot]=d[i][j]; sort(B+1,B+tot+1); tot=unique(B+1,B+tot+1)-(B+1); ll l=1,r=tot,mid,ans; while(l<=r) { mid=(l+r)/2; solve.init(T+1); for(int i=1;i<=n;i++) fcy[i]=0; for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) if(d[j][i]<=B[mid]) solve.AddEdge(i,n+j,inf),fcy[i]+=c[j]; bool F=true; for(int i=1;i<=n;i++) if(fcy[i]<p[i]){ F=false; break;} if(!F) {l=mid+1;continue;} for(int i=1;i<=n;i++) solve.AddEdge(S,i,p[i]); for(int i=1;i<=k;i++) solve.AddEdge(n+i,T,c[i]); ll res=solve.Maxflow(S,T); if(res==sum) r=mid-1,ans=B[mid]; else l=mid+1; } printf("%lld ",ans); }
J .Janitor Troubles
题意:给定ABCD四条边,让你组成一个面积最大的四边形,保证有解。
思路:比赛时三分做的,1A。但是赛后拿去做hduwa掉了,还是没弄清楚。
三分:枚举相邻边的组合形式,然后对对角线进行三分。 结论是当三分到对角线的对面两个角都是直角时,答案最大,此时正好有外接圆。
定理:根据上面结论,相当于四边形是圆内接四边形,我们可以至直接用海伦公式的变形;
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; double X[5],ans; double S(double x,double y,double x1,double y1,double z) { double p=(x+y+z)/2,p1=(x1+y1+z)/2; return sqrt(p*(p-x)*(p-y)*(p-z))+sqrt(p1*(p1-x1)*(p1-y1)*(p1-z));; } double get(int i,int j,int k,int p) { double Mn=max(X[j]-X[i],X[p]-X[k]),Mx=min(X[j]+X[i],X[p]+X[k]); if(Mn>Mx) return 0.0; int T=100; double L=Mn,R=Mx,res=0.0; while(T--){ double Mid1=L+(R-L)/3,Mid2=R-(R-L)/3; double F1=S(X[i],X[j],X[k],X[p],Mid1); double F2=S(X[i],X[j],X[k],X[p],Mid2); if(F1>=F2) res=max(F1,res),R=Mid2; else res=max(F2,res),L=Mid1; } return res; } int main() { rep(i,1,4) scanf("%lf",&X[i]); sort(X+1,X+4+1); rep(i,1,4) rep(j,i+1,4) rep(k,1,4) rep(p,k+1,4) if(i!=k&&i!=p&&j!=k&&j!=p){ ans=max(ans,get(i,j,k,p)); } printf("%.10lf ",ans); return 0; }
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; double X[5],ans; double get() { double res=0.0,p=(X[1]+X[2]+X[3]+X[4])/2; res=sqrt((p-X[1])*(p-X[2])*(p-X[3])*(p-X[4])); return res; } int main() { rep(i,1,4) scanf("%lf",&X[i]); ans=get(); printf("%.10lf ",ans); return 0; }
K .Kingpin Escape
题意:给定一棵有根树,现在让你加最少的边,使得无论原图上哪条边被砍掉,从任意点出发都可以回到根节点。
思路:首先把保证至少两个点与根相邻,所以如果根只有一条边与它相邻,它需要加边。 其次,所有叶子节点需要加边,因为他和父亲被砍断后就GG了。
我们按照一定顺序把这些点连边即可。 假设X个点需要连边,那么最少需要加(X+1)/2条边。然后两两连边即可,但是注意至少一条新加的边连通了根的两个子树。
所以我们按照DFS序,然后q[i]+q[X/2+i]连边,这样可以保证,并不是所有新加的边都在同一个子树(这样可能合法,因为砍掉根与儿子后,子树无法到达根)里。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; int Laxt[maxn],Next[maxn],To[maxn]; int sz[maxn],cnt,q[maxn],tot; void add(int u,int v){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void dfs(int u,int f) { if(sz[u]==1) q[++tot]=u-1; for(int i=Laxt[u];i;i=Next[i]) if(To[i]!=f) dfs(To[i],u); } int main() { int N,M,u,v; scanf("%d%d",&N,&M); M++; rep(i,1,N-1){ scanf("%d%d",&u,&v); u++; v++; sz[u]++; sz[v]++; add(u,v); add(v,u); } dfs(M,0); int ans=(tot+1)/2; printf("%d ",ans); rep(i,1,tot/2) printf("%d %d ",q[i],q[i+tot/2]); if(tot&1) printf("%d %d ",q[1],q[tot]); return 0; }
以上是关于Gym -102007 :Benelux Algorithm Programming Contest (BAPC 18) (寒假自训第5场)的主要内容,如果未能解决你的问题,请参考以下文章
Benelux Algorithm Programming Contest 2014 Final(第二场)
2017 Benelux Algorithm Programming Contest (BAPC 17)
Benelux Algorithm Programming Contest 2019
Benelux Algorithm Programming Contest 2019 (2020-3-21)
Preliminaries for Benelux Algorithm Programming Contest 2019