2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)
Time:2018.4.29 8:30-13:30
A
题意
给出总的路程L,普通速度a,喝咖啡速度后b,等咖啡时间t,咖啡有作用时间r,和n个咖啡厅的位置。买咖啡需要等待,喝咖啡时速度提升
问在哪几个咖啡厅买咖啡,走完总路程所花的时间最短。可以扔掉咖啡,可以用手机预定咖啡。(L<=1e11,a,b<=200,t<=300,r<=1200,n<=5e5)
假如不可以预定怎么做?
分析
Difficult
ym:单调队列优化后dp???,留坑
B solved by ym
题意
四人接力跑,现分别给出n个人的第一棒和其他棒的时间,问选择四个人所用时间最小的时间,和对应的人(即方案) (n<=500)
分析
ym:首先直观的n^4暴力枚举,肯定不行,枚举第一棒贪心选取其余的即可
时间复杂度O(nlogn)
C
题意
分析
大大大模拟,留坑
D solved by ym&czh
题意
给出n个长度为m的0/1串,0和0或者1和1都是相同,问找出一个字符串使得与其他所有串相同的的最多的最少(n<=1e5,m<=20)
分析
ym:相同的数量尽可能少==不同的数量尽可能多,故可以将每个串看做一个点出发从其BFS找所有没有出现过的点,找一个different最大
为什么different最大的是答案?我们BFS找到的每一个点都是原来的点最小different
时间复杂度:O(k*2^k)
czh:将每种字符串看作是一个点,总共2^20=1e6个点,以输入的n个字符串为起点,bfs找到最远的点
这题用了个sting加map,运算起来特别慢,以后要首先分析时间复杂度
#include <cstdio> #include <algorithm> #include <cstring> #include <vector> #include <iostream> #include <cmath> #include <queue> #include <ctime> using namespace std; #define lson l,(l+r)/2,rt<<1 #define rson (l+r)/2+1,r,rt<<1|1 #define dbg(x) cout<<#x<<" = "<< (x)<< endl #define pb push_back #define fi first #define se second #define ll long long #define sz(x) (int)(x).size() #define pll pair<long long,long long> #define pii pair<int,int> #define pq priority_queue int d[1<<20]; queue<int>q; int n,k; void bfs() { while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<k;i++) { int v=u^(1<<i); if(d[v]>d[u]+1) { d[v]=d[u]+1; q.push(v); } } } } int main() { string s; cin>>n>>k; for(int i=0;i<(1<<20);i++) d[i]=1<<20+1; for(int i=1;i<=n;i++) { cin>>s; int sum=0; for(int ii=0;ii<k;ii++) { if(s[ii]=='1') sum+=(1<<(k-ii-1)); } d[sum]=0; q.push(sum); } bfs(); int id=0; for(int i=0;i<(1<<k);i++) { if(d[i]>d[id]) { id=i; } } for(int i=0;i<k;i++) { if((id>>(k-i-1))&1) cout<<1; else cout<<0; } cout<<endl; return 0; }
E solved by ym&czh
题意
n*m 的地方,每个地方给出高度,所有 n*m 的地方有海拔为 0 的积水。 现在给出一个点作为排水口,每个地方的水可以往周围八个方向,且比它低的地方流。问最后会流走多少水
分析
ym:从排水口出发BFS,排水口是终点,每一个点每次只要可以进行类似松弛操作就update,但不幸,TEL,因为一个点可能被update很多次并且每次update后都要从其出发继续update
优化:一个较为显然的是,每次从较低的从出发update最优,思想类似最短路,每次从边权最小的出发
裸的BFS找最短路适用于边权都一样的,才能保证复杂度为O(v+e),因为每个点只会访问一次(第一次访问的一定是最小的),回到此题,高度和边权类似,但“边权”不一样
所以时间复杂度会爆炸
时间复杂度(nlogn)
#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int mod=1e9+7; const int dx[]={1,-1,0,0,1,1,-1,-1}; const int dy[]={0,0,-1,1,-1,1,1,-1}; int mp[505][505]; int h,w,ans[505][505]; int sx,sy; struct node { int x,y,h; friend bool operator<(node a,node b) { return a.h>b.h; } }; bool ok(int x,int y) { if(x>=1&&x<=h&&y>=1&&y<=w) return true; return false; } void bfs(int x,int y,int h) { priority_queue<node>q; q.push({x,y,h}); while(!q.empty()) { node k=q.top(); q.pop(); for(int i=0;i<8;i++) { int xx=k.x+dx[i]; int yy=k.y+dy[i]; if(ok(xx,yy) && ans[xx][yy]>max(k.h,mp[xx][yy])) { ans[xx][yy]=max(k.h,mp[xx][yy]); q.push({xx,yy,ans[xx][yy]}); } } } } int main() { scanf("%d%d",&h,&w); for(int i=1;i<=h;i++) { for(int j=1;j<=w;j++) { scanf("%d",&mp[i][j]); if(mp[i][j]>0) mp[i][j]=0; } } scanf("%d%d",&sx,&sy); if(mp[sx][sy]==0) { cout<<0<<endl; return 0; } ans[sx][sy]=mp[sx][sy]; bfs(sx,sy,mp[sx][sy]); ll answer=0; for(int i=1;i<=h;i++) { for(int j=1;j<=w;j++) { answer+=ans[i][j]; } } cout<<-answer<<endl; return 0; }
F
Different
G solved by czh&ym
题意
有n个队伍,m条事件。每条事件代表a队伍通过一个题目,罚时为b。对于每条事件,输出,1号队伍的排名。
分析
czh:用一个set数组储存每个过题数的队伍编号。对于每次询问,如果不是一号队伍,只需要挪动set中的元素,如果是一号队伍,统计这次过题超过的队伍数,并挪动
ym:写了暴力复杂度爆炸成功TLE,还好帮忙czh找出错误样例
花絮:tm不能当变量名啊!!!
时间复杂度:由于和1同题数最多的队伍很多,并且修改的是1后就不想同的,所以很快(我算不出qaq)
H
题意
分析
ym:听说是极角排序后最大流,滚去学
I solved by ym
题意
问题可以简化为:给出n个点的有向图,问最小环的路径(即方案)(n<=500)
分析
ym:dfs标记找环??X掉) 从每个点出发BFS,求出最短路的过程中记录前驱即可,既然都是环,随便输出都行 / 当然Floyd也可以
dfs找环存在的问题:由于序号标记是按照dfs顺序,故无法得出环的边数,但可以求是否存在环
时间复杂度O(n*(n+m))
#include<bits/stdc++.h> using namespace std; vector<int>v[502],rv[502]; unordered_map<string,int>mp; int d[502]; int pre[502]; char name[502][10]; void bfs(int st) { queue<int>q; q.push(st); d[st]=0; while(!q.empty()) { int now=q.front(); q.pop(); for(int i=0;i<int(v[now].size());i++) { int to=v[now][i]; if(d[to]>d[now]+1) { d[to]=d[now]+1; pre[to]=now; q.push(to); } } } } int main() { int n; string s; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",name[i]); mp[name[i]]=i; } int x=n,k; while(x--) { cin>>s; scanf("%d",&k); int u=mp[s]; while(k--) { cin>>s; while(1) { cin>>s; int len=s.length(); if(s[len-1]==',') { s.resize(len-1); int to=mp[s]; v[to].push_back(u); rv[u].push_back(to); } else { int to=mp[s]; v[to].push_back(u); rv[u].push_back(to); break; } } } } int ans=1e9,st,ed; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) d[j]=1e9; bfs(i); for(int j=0;j<int(rv[i].size());j++) { int u=rv[i][j]; if(ans>d[u]+1) { ans=d[u]+1; st=i; ed=u; } } } if(ans==1e9) { puts("SHIP IT"); return 0; } else { for(int j=1;j<=n;j++) d[j]=1e9; bfs(st); int now=ed; while(now!=st) { printf("%s ",name[now]); now=pre[now]; } printf("%s\n",name[now]); } return 0; }
J solved by ym
题意
签到,按题意模拟即可
K solved by ym
题意
给出三种人的数量b,n,e,每种人有一个Sb,Sn,Se,现两人一组,给出(b+n+e)/2个vi,问要所有组最小的最大方案,输出最小的最大
分析
ym:二分答案后贪心暴力check
时间复杂度O(nlogn)
#include<bits/stdc++.h> using namespace std; int B,N,E; int a[1000005],w[5],m[5]; int sb,sn,se,n; bool check(int mid) { int mm[5]; mm[1]=m[1], mm[2]=m[2], mm[3]=m[3]; for(int i=1;i<=n;i++) { int jj=-1,kk=-1; int need=mid/a[i]+(mid%a[i]>0); for(int j=1;j<=3;j++) { for(int k=j;k<=3;k++) { int num=1; if(j==k) num=2; if(jj==-1&&kk==-1) { if(mm[j]>=num && mm[k]>=num && (w[j]+w[k])>=need) { jj=j; kk=k; } } else { if(mm[j]>=num && mm[k]>=num && (w[j]+w[k])>=need && (w[j]+w[k])<(w[jj]+w[kk])) { jj=j; kk=k; } } } } if(jj==-1) return false; mm[jj]-=1; mm[kk]-=1; } return true; } int main() { scanf("%d%d%d",&m[1],&m[2],&m[3]); scanf("%d%d%d",&w[1],&w[2],&w[3]); n=(m[1]+m[2]+m[3])/2; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } int l=1,r=1e9; while(l<r) { int mid=(l+r+1)>>1; if(check(mid)) l=mid; else r=mid-1; } printf("%d\n", l); return 0; }
Summary
Ym:
Czh: