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\n",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\n",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; }
E .Entirely Unsorted Sequences
https://blog.csdn.net/ccsu_cat/article/details/86754931
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\n",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\n",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\n",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\n",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\n",ans); rep(i,1,tot/2) printf("%d %d\n",q[i],q[i+tot/2]); if(tot&1) printf("%d %d\n",q[1],q[tot]); return 0; }