TZOJ 挑战题库随机训练01
A.Toys回到顶部
题意
贝西生日到了,他想庆祝D([1,10^5])天,第i天需要Ti([1,50])个玩具庆祝,1个玩具Tc([1,60])美元,玩具使用过了会变脏,消毒后可以再次使用
消毒方式1:1个玩具消毒N1([1,D])天,花费C1([1,60])美元
消毒方式2:1个玩具消毒N2([1,D])天,花费C2([1,60])美元
问怎么计划使得花费最少?
PS:庆祝273年,牛逼
题解
分析一下,设最小花费是f(x),x为总共买了多个玩具
显然f(x-1)>=f(x)<=f(x+1),少买或多买1个玩具会使得f变大
存在这样一种情况,买了太少的玩具使得怎样都不能满足计划,可设f(x)=inf
单峰函数存在最小值,可用三分解决,这里三分有一个细节
三分lmid=(l+r)/2,rmid=(lmid+r)/2,当lmid=rmid相等时,r-l<=2,如果移动r=rmid,假设答案恰好在r,那么完蛋
也就是说,我们需要保证r-l>2的时候才继续三分,最后[l,r]区间重新算一遍
那么问题就变成已知买了x个玩具求最小花费
显然可以贪心,我们让N1<N2,C1>C2,如果花费时间长还贵,完全可以让C2=C1
那么先用花费时间长N2,再用花费时间短的N1,还不行用x,再不行无解
该如何实现呢?
开3个双端队列,表示等待被消毒,可以用短时间消毒,可以用长时间消毒,代码1,复杂度O(Σalogn),2s接近tle
我们发现第一种方法贪心,每次放进入a[i]个待消毒,Σa复杂度很高
如何优化呢,设q[i]表示第i天剩下的,假设当前处理第i天,i-N2<=i-N1为两个时间节点
对于花费时间长的,维护一个指针top1,当top1<=i-N2可以用min(q[top1],x-a[i])个
对于花费时间短的,相当于top2=i-N1节点往前到top1可以用,发现区间[top1,i-N1]中可能某个为q[i]=0,如果不管,复杂度退化到n^2logn,
发现区间内的值只会不断减小到0,而且要么减成0,要么减成1个值,然后结束,那么就可以用并查集维护向前跳的节点直到top2<top1
代码2,复杂度O(nlognlogn),0.2s
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=1e5+5; 5 int a[N]; 6 int d,n1,n2,c1,c2,tc,sum; 7 deque<int>ti,ts,tl; 8 //ti 需要被消毒的数量 9 //ts 花费时间n1少的 c1元(昂贵) 10 //tl 花费事件n2长的 c2元(便宜) 11 int cal(int buy){ 12 int sumc=buy*tc; 13 ti.clear();ts.clear();tl.clear(); 14 for(int i=1;i<=d;i++){ 15 int bought=0; 16 if(buy>=a[i])bought=a[i],buy-=a[i]; 17 else bought=buy,buy=0; 18 while(!ti.empty()&&i-ti.front()>=n1){ 19 ts.push_back(ti.front()); 20 ti.pop_front(); 21 } 22 while(!ts.empty()&&i-ts.front()>=n2){ 23 tl.push_back(ts.front()); 24 ts.pop_front(); 25 } 26 while(bought<a[i]){ 27 if(!tl.empty())tl.pop_back(),bought++,sumc+=c2; 28 else if(!ts.empty())ts.pop_back(),bought++,sumc+=c1; 29 else return 1e9; 30 } 31 for(int j=1;j<=a[i];j++)ti.push_back(i); 32 } 33 return sumc; 34 } 35 int main(){ 36 while(scanf("%d%d%d%d%d%d",&d,&n1,&n2,&c1,&c2,&tc)!=EOF){ 37 if(n1>n2){ 38 swap(n1,n2); 39 swap(c1,c2); 40 } 41 //花费时间长而且还贵,那肯定不要了 42 if(c2>c1)c2=c1; 43 sum=0; 44 for(int i=1;i<=d;i++){ 45 scanf("%d",&a[i]); 46 sum+=a[i]; 47 } 48 int l=a[1],r=sum,lmid,rmid; 49 while(r-l>2){ 50 lmid=(l+r)>>1; 51 rmid=(lmid+r)>>1; 52 int lval=cal(lmid),rval=cal(rmid); 53 //printf("l=%d lmid=%d rmid=%d r=%d\n",l,lmid,rmid,r); 54 //printf("lval=%d rval=%d\n",lval,rval); 55 if(rval!=1e9&&lval<rval)r=rmid; 56 else l=lmid; 57 } 58 int ans=1e9; 59 for(int i=l;i<=r;i++)ans=min(ans,cal(i)); 60 printf("%d\n",ans); 61 } 62 return 0; 63 } 64 /* 65 4 1 3 49 23 59 66 13 67 37 68 26 69 37 70 */
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int maxn=100010,inf=~0U>>1; 6 7 int n,N1,N2,C1,C2,Tc; 8 int a[maxn],asum; 9 int ans; 10 11 int q[maxn],top1,top2; 12 int fa[maxn]; 13 14 int Find(int x) {return (fa[x]==x)?x:fa[x]=Find(fa[x]);} 15 16 int f(int x) 17 { 18 for (int i=1;i<=n;i++) q[i]=a[i],fa[i]=i; 19 top1=1;top2=0; 20 int res=Tc*x; 21 for (int i=1;i<=n;i++) 22 { 23 top2=i-N1; 24 if (x<a[i]) 25 { 26 while (top1<=i-N2 && x<a[i]) 27 { 28 int m=min(q[top1],a[i]-x); 29 x+=m; 30 q[top1]-=m; 31 res+=C2*m; 32 if (x<a[i]) top1++; 33 } 34 while (top2>=top1 && x<a[i]) 35 { 36 int m=min(q[top2],a[i]-x); 37 x+=m; 38 q[top2]-=m; 39 res+=C1*m; 40 if (x<a[i]) fa[top2]=Find(top2-1),top2=fa[top2]; 41 } 42 if (x<a[i]) return inf; 43 } 44 x-=a[i]; 45 } 46 return res; 47 } 48 49 int main() 50 { 51 scanf("%d%d%d%d%d%d",&n,&N1,&N2,&C1,&C2,&Tc); 52 if (N1>N2) swap(N1,N2),swap(C1,C2); 53 if (C1<C2) C2=C1; 54 for (int i=1;i<=n;i++) scanf("%d",&a[i]),asum+=a[i]; 55 int l=0,r=asum; 56 while (r-l>2) 57 { 58 int m1=l+(r-l)/3,m2=l+2*((r-l)/3); 59 int fm1=f(m1),fm2=f(m2); 60 if (fm1!=inf && fm1<=fm2) r=m2;else l=m1; 61 } 62 ans=inf; 63 for (int i=l;i<=r;i++) ans=min(ans,f(i)); 64 printf("%d\n",ans); 65 return 0; 66 }
B.Tanning Salon回到顶部
题意
n([1,20])张床,大写字母序列s,字母成对出现,前一个表示进来,后一个表示离开,保证不存在离开晚于有人用完床离开
题解
unordered_map<int,int> ma维护字母是否在使用床
c[i]=1表示字母i进来,0表示i没进来
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 5 int n;string s; 6 while(cin>>n,n){ 7 cin>>s; 8 int away=0; 9 int c[26]={0}; 10 unordered_map<int,int>ma; 11 for(int i=0;s[i];i++){ 12 if(ma.count(s[i])){ 13 ma.erase(s[i]); 14 }else{ 15 if(ma.size()==n){ 16 if(c[s[i]-'A']==1)away++; 17 c[s[i]-'A']=1-c[s[i]-'A']; 18 } 19 else ma[s[i]]=1; 20 } 21 } 22 if(away==0)cout<<"All customers tanned successfully.\n"; 23 else cout<<away<<" customer(s) walked away.\n"; 24 } 25 return 0; 26 }
C.数据结构―线性表插入删除回到顶部
题意
给定一个线性表,以及若干的插入和删除操作后,输出最终的结果
题解
list基本操作
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main(){ 5 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 6 int t,n,x,q,m,k;string s; 7 cin>>t; 8 while(t--){ 9 list<int>li; 10 cin>>n; 11 for(int i=1;i<=n;i++){ 12 cin>>x; 13 li.push_back(x); 14 } 15 list<int>::iterator it; 16 cin>>q; 17 while(q--){ 18 cin>>s>>m; 19 it=li.begin(); 20 while(--m)it++; 21 list<int>up; 22 if(s[0]=='i'){ 23 cin>>k; 24 for(int i=1;i<=k;i++){ 25 cin>>x; 26 up.push_back(x); 27 } 28 li.insert(it,up.begin(),up.end()); 29 }else{ 30 cin>>k; 31 while(k--&&it!=li.end())it=li.erase(it); 32 } 33 } 34 for(it=li.begin();it!=li.end();++it){ 35 if(it!=li.begin())cout<<" "; 36 cout<<*it; 37 } 38 cout<<endl; 39 } 40 }
D.离散――真值表是个好办法回到顶部
题意
给不超过20个括号组,字母数([1,10]),组间用^,v连接,组内用^,v,->,<->,输出主析取范式和主合取范式
题解
模拟,先处理括号内的每个取值(离散符号不懂的自行百度),再处理外的
PS:一度怀疑数据,感谢詹老哥
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct node{ 4 char l,s,r; 5 // s='-'<-> s='>'-> s='^' s='v' 6 }; 7 int main(){ 8 int n; 9 while(cin>>n,n){ 10 string s; 11 cin>>s; 12 vector<node>v; 13 vector<char>en; 14 for(int i=0;i<s.size();i++){ 15 if(s[i]=='('){ 16 node ne; 17 if(i+4<s.size()&&s[i+4]==')'){ 18 ne.l=s[i+1];ne.s=s[i+2];ne.r=s[i+3]; 19 }else if(i+5<s.size()&&s[i+5]==')'){ 20 ne.l=s[i+1];ne.s='>';ne.r=s[i+4]; 21 }else if(i+6<s.size()&&s[i+6]==')'){ 22 ne.l=s[i+1];ne.s='-';ne.r=s[i+5]; 23 } 24 v.push_back(ne); 25 }else if(s[i-1]==')'&&(s[i]=='^'||s[i]=='v')){ 26 en.push_back(s[i]); 27 } 28 } 29 int sta=1<<n,cnt=0; 30 bool vis[1030]={0}; 31 for(int i=0;i<sta;i++){ 32 int val[15]; 33 for(int j=0;j<n;j++){ 34 if((1<<j)&i)val[n-j-1]=1; 35 else val[n-j-1]=0; 36 } 37 vector<int>real; 38 for(int j=0;j<v.size();j++){ 39 int hasl=val[v[j].l-'A'],hasr=val[v[j].r-'A']; 40 if(v[j].s=='v'&&(hasl||hasr))real.push_back(1); 41 else if(v[j].s=='^'&&hasl&&hasr)real.push_back(1); 42 else if(v[j].s=='-'&&hasl==hasr)real.push_back(1); 43 else if(v[j].s=='>'&&(!hasl||(hasl&&hasr)))real.push_back(1); 44 else real.push_back(0); 45 } 46 stack<int>pri; 47 stack<char>po; 48 pri.push(real[0]); 49 for(int j=1;j<real.size();j++){ 50 pri.push(real[j]); 51 if(en[j-1]=='^'){ 52 int v1=pri.top();pri.pop(); 53 int v2=pri.top();pri.pop(); 54 pri.push(v1&v2); 55 }else{ 56 po.push(en[j-1]); 57 } 58 } 59 while(!po.empty()){ 60 int v1=pri.top();pri.pop(); 61 int v2=pri.top();pri.pop(); 62 char en1=po.top();po.pop(); 63 pri.push(v1|v2); 64 } 65 if(pri.top()){ 66 vis[i]=1;cnt++; 67 } 68 } 69 int out; 70 if(cnt==0){ 71 cout<<"none\n"; 72 }else{ 73 out=0; 74 for(int i=0;i<sta;i++){ 75 if(!vis[i])continue; 76 if(out)cout<<"v"; 77 if(out==0)out=1; 78 cout<<"m"<<i; 79 } 80 cout<<endl; 81 } 82 if(cnt==sta){ 83 cout<<"none\n"; 84 }else{ 85 out=0; 86 for(int i=0;i<sta;i++){ 87 if(vis[i])continue; 88 if(out)cout<<"^"; 89 if(out==0)out=1; 90 cout<<"M"<<i; 91 } 92 cout<<endl; 93 } 94 } 95 return 0; 96 }
E.表达式求值回到顶部
题意
给一些包含加减号和小括号的表达式,求出该表达式的值。表达式中的数值均为绝对值小于10的整数
题解
模拟,把负数先处理出来,再用栈处理剩下的
PS:代码比较暴躁
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int t; 7 string s; 8 cin>>t; 9 while(t--){ 10 cin>>s; 11 vector<int>val; 12 int begin=0; 13 for(int i=0;i<s.size();i++){ 14 val.push_back(s[i]); 15 if(s[i]=='-'&&'0'<=s[i+1]&&s[i+1]<='9'){ 16 if('0'<=s[i-1]&&s[i-1]<='9'||s[i-1]==')'); 17 else{ 18 val.pop_back(); 19 val.push_back(-s[i+1]); 20 i++; 21 } 22 } 23 } 24 for(int i=0;i<val.size();i++){ 25 if(val[i]<0)val[i]=-(-val[i]-'0'); 26 else if('0'<=val[i]&&val[i]<='9')val[i]=val[i]-'0'; 27 } 28 stack<int>dig,sign; 29 int a,b; 30 for(int i=0;i<val.size();i++){ 31 if(abs(val[i])<=9){ 32 dig.push(val[i]); 33 }else if(val[i]=='+'){ 34 if(abs(val[i+1])<=9){ 35 a=dig.top();dig.pop(); 36 //printf("push a=%d + %d val=%d\n",a,val[i+1],a+val[i+1]); 37 dig.push(a+val[i+1]); 38 i++; 39 }else{ 40 //printf("push +\n"); 41 sign.push('+'); 42 } 43 }else if(val[i]=='-'){ 44 if(abs(val[i+1])<=9){ 45 a=dig.top();dig.pop(); 46 //printf("push a=%d - %d val=%d\n",a,val[i+1],a-val[i+1]); 47 dig.push(a-val[i+1]); 48 i++; 49 }else{ 50 //printf("push -\n"); 51 sign.push('-'); 52 } 53 }else if(val[i]==')'){ 54 //printf(") %c\n",sign.top()); 55 while(sign.top()!='('){ 56 int si=sign.top();sign.pop(); 57 a=dig.top();dig.pop(); 58 b=dig.top();dig.pop(); 59 //printf("?? %d %c %d\n",a,char(si),b); 60 if(si=='+')dig.push(b+a); 61 else if(si=='-')dig.push(b-a); 62 } 63 sign.pop(); 64 if(!sign.empty()&&sign.top()=='-'){ 65 a=dig.top();dig.pop(); 66 sign.pop(); 67 sign.push('+'); 68 dig.push(-a); 69 } 70 }else if(val[i]=='('){ 71 //printf("(\n"); 72 sign.push('('); 73 } 74 } 75 while(!sign.empty()){ 76 int si=sign.top();sign.pop(); 77 a=dig.top();dig.pop(); 78 b=dig.top();dig.pop(); 79 //printf("%d %c %d\n",a,char(si),b); 80 if(si=='+')dig.push(b+a); 81 else if(si=='-')dig.push(b-a); 82 } 83 cout<<dig.top()<<endl; 84 } 85 return 0; 86 }
F.Circle VS Triangle回到顶部
题意
判断圆是否在三角形内部
题解
求圆心到三条边的最短距离,跟r比一下
代码
1 //#include<bits/stdc++.h> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 9 typedef long long LL; 10 11 const double eps=1e-8; 12 const double pi=acos(-1.0); 13 const int inf=0x7f7f7f7f; 14 15 inline int dcmp(const double &x){ 16 return x>eps?1:(x<-eps?-1:0); 17 } 18 inline double sqr(const double &x){ 19 return x*x; 20 } 21 struct Point{ 22 double x,y; 23 Point(double x=0,double y=0):x(x),y(y){} 24 Point operator-(const Point &rhs)const{ 25 return Point(x-rhs.x,y-rhs.y); 26 } 27 Point operator+(const Point &rhs)const{ 28 return Point(x+rhs.x,y+rhs.y); 29 } 30 Point operator/(double scale)const{ 31 return Point(x/scale,y/scale); 32 } 33 Point operator*(double scale)const{ 34 return Point(x*scale,y*scale); 35 } 36 double operator*(const Point &rhs)const{ 37 return x*rhs.x+y*rhs.y; 38 } 39 double operator^(const Point &rhs)const{ 40 return x*rhs.y-y*rhs.x; 41 } 42 Point operator-=(const Point &rhs){ 43 return *this=*this-rhs; 44 } 45 Point operator+=(const Point &rhs){ 46 return *this=*this+rhs; 47 } 48 Point operator/=(double scale){ 49 return *this=*this/scale; 50 } 51 Point operator*=(double scale){ 52 return *this=*this*scale; 53 } 54 bool operator<(const Point &rhs)const{ 55 return dcmp(x-rhs.x)<0||(dcmp(x-rhs.x)==0&&dcmp(y-rhs.y)<0); 56 } 57 bool operator==(const Point &rhs)const{ 58 return dcmp(x-rhs.x)==0&&dcmp(y-rhs.y)==0; 59 } 60 double vlen()const{ 61 return sqrt(sqr(x)+sqr(y)); 62 } 63 Point normal()const{ 64 double L=this->vlen(); 65 return Point(-y/L,x/L); 66 } 67 }; 68 typedef Point Vector; 69 double Dot(Vector A,Vector B){ 70 return A*B; 71 } 72 double Cross(Vector A,Vector B){ 73 return A^B; 74 } 75 //点到线段的距离 76 double DistanceToSegment(Point P,Point A,Point B){ 77 if(A==B)return (P-A).vlen(); 78 Vector v1=B-A,v2=P-A,v3=P-B; 79 if(dcmp(Dot(v1,v2))<0)return v2.vlen(); 80 else if(dcmp(Dot(v1,v3))>0)return v3.vlen(); 81 else return fabs(Cross(v1,v2))/v1.vlen(); 82 } 83 int main(){ 84 Point p[4]; 85 while(scanf("%lf%lf%lf%lf%lf%lf",&p[1].x,&p[1].y,&p[2].x,&p[2].y,&p[3].x,&p[3].y)!=EOF){ 86 Point cir;double r; 87 scanf("%lf%lf%lf",&cir.x,&cir.y,&r); 88 if(DistanceToSegment(cir,p[1],p[2])>=r&&DistanceToSegment(cir,p[2],p[3])>=r&& 89 DistanceToSegment(cir,p[1],p[3])>=r)printf("YES\n"); 90 else printf("NO\n"); 91 } 92 return 0; 93 }
G.Hot girl with cool car回到顶部
题意
下图,w代表轨道宽度,r代表转弯半径(轨道内部),θ([0,180])代表曲线角度,求最大转弯半径R
题解
PS:詹老哥牛逼
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const double PI=acos(-1.0); 4 int main(){ 5 double r,w,s; 6 while(scanf("%lf%lf%lf",&r,&w,&s)!=EOF){ 7 double c=cos(s*PI/360.0); 8 printf("%.3f\n",(r+w-c*r)/(1-c)); 9 } 10 return 0; 11 }
H.紧急援救回到顶部
题意
n([1,1000])个路口,m([1,10000])条单向边,s个案件,每个案件k辆警车,目的地c,问其中一辆到目的地的最小时间
题解
裸dijstra
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1005; 4 vector< pair<int,double> >G[N]; 5 double d[N]; 6 queue<int>q; 7 double dij(int t){ 8 while(!q.empty()){ 9 int u=q.front();q.pop(); 10 for(int i=0;i<G[u].size();i++){ 11 int v=G[u][i].first; 12 double w=G[u][i].second; 13 if(d[v]>d[u]+w){ 14 d[v]=d[u]+w; 15 q.push(v); 16 } 17 } 18 } 19 if(d[t]==1e18)return -1; 20 else return d[t]; 21 } 22 int main(){ 23 int n,m,s; 24 scanf("%d%d%d",&n,&m,&s); 25 for(int i=1;i<=m;i++){ 26 int s1,t1;double c1; 27 scanf("%d%d%lf",&s1,&t1,&c1); 28 G[s1].push_back({t1,c1}); 29 } 30 int ca=1; 31 while(s--){ 32 int c,k,car; 33 scanf("%d%d",&c,&k); 34 for(int i=1;i<=n;i++)d[i]=1e18; 35 while(!q.empty())q.pop(); 36 for(int i=1;i<=k;i++){ 37 scanf("%d",&car); 38 q.push(car); 39 d[car]=0.0; 40 } 41 printf("Scenario %d:\n",ca++); 42 double ans=dij(c); 43 if(ans==-1)printf("Impossible.\n"); 44 else printf("%.2f\n",ans); 45 puts(""); 46 } 47 return 0; 48 }
I.Fruit回到顶部
题意
n([1,100])个不同水果,购买个数限制[Ai,Bi]([0,100]),问买m([1,100])个水果几种不同方法,数据保证在int内
题解
dp[i][j]代表第i个水果,已经买了j个的方案数
枚举再买j个,已经有k个,dp[i][k+j]+=dp[i-1][k]
复杂度O(n^3)
PS:原数据有爆int,已修正
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 5 int dp[105][105]; 6 int n,m; 7 while(cin>>n>>m){ 8 memset(dp,0,sizeof dp); 9 dp[0][0]=1; 10 for(int i=1;i<=n;i++){ 11 int a,b; 12 cin>>a>>b; 13 for(int j=a;j<=b;j++) 14 for(int k=0;k<=m-j;k++) 15 dp[i][k+j]+=dp[i-1][k]; 16 } 17 cout<<dp[n][m]<<'\n'; 18 } 19 return 0; 20 }
J.Family planning回到顶部
题意
父母最多生M([0,30])个,生了N([0,20])个,若生过男则再生罚款,若超生罚款,罚款1W,2W,4W......
题解
模拟,注意爆int
PS:生20个恐怖如斯
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 int main(){ 5 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 6 int t; 7 cin>>t; 8 while(t--){ 9 int m,n; 10 cin>>m>>n; 11 int f=0; 12 LL sum=0,up=10000; 13 for(int i=1;i<=n;i++){ 14 int x; 15 cin>>x; 16 if(f||!m)sum+=up,up+=up; 17 if(m)m--; 18 if(x==1)f=1; 19 } 20 cout<<sum<<" RMB\n"; 21 } 22 return 0; 23 }