TZOJ 挑战题库随机训练11
A.The Table回到顶部
题意
n行m列,问哪一列乘积最大,相同输出m大的,n<=1000,m<=20
题解
曾经做过(07G),大数相乘处理一下就行了,复杂度O(nm)
代码
1 import java.math.BigInteger; 2 import java.util.*; 3 4 public class Main { 5 6 public static void main(String[] args) { 7 8 BigInteger[] big = new BigInteger[25]; 9 10 Scanner sc = new Scanner(System.in); 11 while(sc.hasNext()) { 12 int m = sc.nextInt(); 13 int n = sc.nextInt(); 14 BigInteger b; 15 for (int i = 1; i <= m; i++) 16 big[i] = BigInteger.ONE; 17 for (int i = 1; i <= n; i++) { 18 for (int j = 1; j <= m; j++) { 19 b = sc.nextBigInteger(); 20 big[j] = big[j].multiply(b); 21 } 22 } 23 int id = 1; 24 BigInteger maxx = big[1]; 25 for (int i = 2; i <= m; i++) { 26 //System.out.println(maxx.compareTo(big[i])); 27 if (maxx.compareTo(big[i]) <= 0) { 28 maxx = big[i]; 29 id = i; 30 } 31 } 32 System.out.println(id); 33 } 34 } 35 }
B.单身狗沙漠逃生回到顶部
题意
每走x公里路后,单身狗就要消耗x份食物(x为大于0的分数)。单身狗共准备了n份食物,但他身上最多背负m份食物(n%m为0,而且n/m<11)。如果食物可以在路边存放,为保证能原路返回出发点,单身狗最远能进入沙漠多少公里?
题解
一共走m/n次,第一次(来回)m/2,第二次m/4,以此类推,复杂度O(m/n)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main(){ 5 int n,m; 6 while(cin>>n>>m,n||m){ 7 int s=0,x=2*2520; 8 for(int i=1;i<=n/m;i++)s+=m*(2520/i); 9 int gc=__gcd(s,x); 10 cout<<s/gc<<'/'<<x/gc<<endl; 11 } 12 return 0; 13 }
C.树的遍历回到顶部
题意
给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数,n<=30
题解
后序(左右根),中序(左根右)
递归建树,后序第一个节点为根,然后在中序找到这个根,左右子树分开,复杂度O(n)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=1005; 5 int a[N],b[N]; 6 vector<int>G[35]; 7 void build(int L1,int R1,int L2,int R2,int dep){ 8 if(L1>R1)return; 9 G[dep].push_back(a[R1]); 10 for(int i=L2;i<=R2;i++)if(a[R1]==b[i]){ 11 build(L1,L1-1-L2+i,L2,i-1,dep+1); 12 build(R1-R2+i,R1-1,i+1,R2,dep+1); 13 } 14 } 15 int main(){ 16 int n;scanf("%d",&n); 17 for(int i=1;i<=n;i++)cin>>a[i]; 18 for(int i=1;i<=n;i++)cin>>b[i]; 19 build(1,n,1,n,0); 20 int out=0; 21 for(int i=0;i<30;i++)for(int j=0;j<G[i].size();j++){ 22 if(out)cout<<' '; 23 cout<<G[i][j];out=1; 24 } 25 return 0; 26 }
D.Convert QWERTY to Dvorak回到顶部
题意
给一串键盘A要求输出键盘B,len<=100
题解
对应映射存起来,再处理,复杂度O(70len)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 string a="-=_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'ASDFGHJKL:\"zxcvbnm,./ZXCVBNM<>?"; 5 string b="[]{}',.pyfgcrl/=\"<>PYFGCRL?+aoeuidhtns-AOEUIDHTNS_;qjkxbmwvz:QJKXBMWVZ"; 6 int main(){ 7 string s; 8 while(getline(cin,s)){ 9 for(int i=0;s[i];i++){ 10 bool f=0; 11 for(int j=0;a[j];j++) 12 if(s[i]==a[j]){ 13 f=1;cout<<b[j]; 14 } 15 if(f==0)cout<<s[i]; 16 } 17 cout<<endl; 18 } 19 return 0; 20 }
E.Formula Racing回到顶部
题意
给一张n*m的图,W代表墙,s代表起跑线/目标线,.代表非跑道,x代表跑道
d辆车,给你起点方向和最大速度,在给一串操作序列
m代表前进,a代表速度+1再前进,b代表速度-1再前进,l代表左转再前进,r代表右转再前进
如果小车到了.,速度降为1
每辆车输出,如果到了s,输出crossing startline
如果小车没有撞墙输出终点方向速度
如果小车撞墙了就停止crashed
n,m<=1000,d<=7,len<=10000
题解
模拟每辆小车的操作,按照要求处理,复杂度O(sumlend)
坑1:注意n行m列,列代表y,行代表x,在判断的时候判断的是G[y][x]
坑2:如果小车最后撞墙,之前如果经过s也要输出crossing startline
PS:题意有点难理解
代码
1 #include<stdio.h> 2 #include<string.h> 3 #include<vector> 4 using namespace std; 5 6 const int N=1005; 7 8 char G[N][N]; 9 int dx[]={0,1,1,1,0,-1,-1,-1}; 10 int dy[]={-1,-1,0,1,1,1,0,-1}; 11 struct node{ 12 int ux,uy,dir,sp,i; 13 }; 14 int main(){ 15 int t,n,m,q; 16 scanf("%d",&t); 17 for(int ca=1;ca<=t;ca++){ 18 scanf("%d%d",&n,&m); 19 for(int i=0;i<n;i++)scanf("%s",G[i]); 20 scanf("%d",&q); 21 printf("Scenario #%d:\n",ca); 22 while(q--){ 23 int ux,uy,dir,spm,sp=0;char op[10005]; 24 scanf("%d%d%d%d%s",&ux,&uy,&dir,&spm,op); 25 //printf("%d %d %d %d %s\n",ux,uy,dir,spm,op); 26 vector<node>vec; 27 int len=strlen(op); 28 for(int i=0;i<len;i++){ 29 if(op[i]=='a'&&sp<spm)sp++; 30 if(op[i]=='b'&&sp>0)sp--; 31 if(op[i]=='l')dir=(dir+7)%8; 32 if(op[i]=='r')dir=(dir+1)%8; 33 int up=0; 34 for(int j=1;j<=sp;j++){ 35 ux+=dx[dir],uy+=dy[dir]; 36 //printf("j=%d ux=%d uy=%d\n",j,ux,uy); 37 if(G[uy][ux]=='W'){ 38 printf("%d %d %d %d crashed\n",ux,uy,dir,sp); 39 goto e; 40 } 41 if(G[uy][ux]=='.')up=1; 42 if(G[uy][ux]=='s')vec.push_back((node){ux,uy,dir,sp,i}); 43 } 44 //printf("i=%d (%d,%d) dir=%d sp=%d\n",i,ux,uy,dir,sp); 45 if(up)sp=1; 46 } 47 printf("%d %d %d %d\n",ux,uy,dir,sp); 48 e:; 49 for(int i=0;i<vec.size();i++) 50 printf("crossing startline: %d %d %d %d %d\n",vec[i].ux,vec[i].uy,vec[i].dir,vec[i].sp,vec[i].i); 51 } 52 printf("\n"); 53 } 54 return 0; 55 }
F.Helping Florida回到顶部
题意
n个候选人,m张选票,每轮选举淘汰票数最少的(可以有多个最少),对于选票来说如果这个候选人被淘汰,那么就推到下一个候选人
选票的结构是,候选人1,候选人2,候选人3......
到最后一轮或者候选者有>=(m+1)/2张票,对应的候选者获胜,可以有多个获胜
题解
模拟每轮被淘汰的候选人,注意处理选票的时候不要越界了,复杂度O(nm)
PS:唯一获胜XXX won,并列获胜it is a tie between XXX and XXX
PS:题意又是有点难理解
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 map<string,int>votes; 5 vector<string>vec[105]; 6 int tail[105]; 7 int main(){ 8 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 9 int n,m;string s,ss; 10 while(cin>>n>>m,n||m){ 11 votes.clear(); 12 for(int i=1;i<=m;i++)vec[i].clear(),tail[i]=0; 13 cin.get(); 14 for(int i=1;i<=m;i++){ 15 getline(cin,ss); 16 stringstream s(ss); 17 while(s>>ss)vec[i].push_back(ss); 18 } 19 map<string,int>dead; 20 for(int i=1;i<=m;i++)votes[vec[i][0]]++; 21 int t=0,MinVo,MaxVo; 22 while(1){ 23 MinVo=1e9;MaxVo=0; 24 for(auto X:votes)MinVo=min(MinVo,X.second),MaxVo=max(MaxVo,X.second); 25 if(MaxVo>=(m+1)/2){ 26 t=1; 27 break; 28 } 29 vector<string>del; 30 for(auto X:votes)if(MinVo==X.second)del.push_back(X.first); 31 if(del.size()==votes.size())break; 32 for(auto X:del){ 33 dead[X]=1,votes.erase(X); 34 } 35 int rup=0; 36 for(int i=1;i<=m;i++){ 37 int up=0; 38 while(tail[i]<vec[i].size()&&dead.count(vec[i][tail[i]]))tail[i]++,up=1; 39 if(tail[i]<vec[i].size()&&up)rup=1,votes[vec[i][tail[i]]]++; 40 } 41 if(rup==0)break; 42 } 43 vector<string>out; 44 if(t){ 45 for(auto X:votes)if(X.second==MaxVo)out.push_back(X.first); 46 } 47 else{ 48 for(auto X:votes)out.push_back(X.first); 49 } 50 if(out.size()==1)cout<<out[0]<<" won\n"; 51 else{ 52 cout<<"it is a tie between "<<out[0]; 53 for(int i=1;i<out.size();i++) 54 cout<<" and "<<out[i]; 55 cout<<'\n'; 56 } 57 } 58 return 0; 59 }
G.Double Shortest Paths回到顶部
题意
给一张有向图,边(u,v,a,b)第一次经过花费a,第二次经过花费a+b,问从1->n走两次的最小花费,n<=500,m<=2000
题解
最小费用最大流模板题,超级源点S(0)->1流量2花费0,n->超级汇点T(n+1)流量2花费0,其余(u,v,a,b),连两条,第一条u->v流量1花费a,第二条u->v流量1花费a+b,复杂度玄学(一般n<=500)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=1e5+5; 5 const int M=2e5+5; 6 const int INF=0x3f3f3f3f; 7 8 int FIR[N],FROM[M],TO[M],CAP[M],FLOW[M],COST[M],NEXT[M],tote; 9 int pre[N],dist[N],q[400000]; 10 bool vis[N]; 11 int n,m,S,T; 12 void init() 13 { 14 tote=0; 15 memset(FIR,-1,sizeof(FIR)); 16 } 17 void addEdge(int u,int v,int cap,int cost) 18 { 19 FROM[tote]=u; 20 TO[tote]=v; 21 CAP[tote]=cap; 22 FLOW[tote]=0; 23 COST[tote]=cost; 24 NEXT[tote]=FIR[u]; 25 FIR[u]=tote++; 26 27 FROM[tote]=v; 28 TO[tote]=u; 29 CAP[tote]=0; 30 FLOW[tote]=0; 31 COST[tote]=-cost; 32 NEXT[tote]=FIR[v]; 33 FIR[v]=tote++; 34 } 35 bool SPFA(int s, int t)//寻找花销最少的路径 36 { 37 //跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流 38 //若存在 说明可以继续增广,反之不能 39 memset(dist,INF,sizeof(dist)); 40 memset(vis,false,sizeof(vis)); 41 memset(pre,-1,sizeof(pre)); 42 dist[s] = 0;vis[s]=true;q[1]=s; 43 int head=0,tail=1; 44 while(head!=tail) 45 { 46 int u=q[++head];vis[u]=false; 47 for(int v=FIR[u];v!=-1;v=NEXT[v]) 48 { 49 if(dist[TO[v]]>dist[u]+COST[v]&&CAP[v]>FLOW[v])//可以松弛 且 没有满流 50 { 51 dist[TO[v]]=dist[u]+COST[v]; 52 pre[TO[v]]=v;//记录前驱边 的编号 53 if(!vis[TO[v]]) 54 { 55 vis[TO[v]] = true; 56 q[++tail]=TO[v]; 57 } 58 } 59 } 60 } 61 return pre[t]!=-1;//可达返回true 62 } 63 void MCMF(int s, int t, int &cost, int &flow) 64 { 65 flow=0;//总流量 66 cost=0;//总费用 67 while(SPFA(s,t))//每次寻找花销最小的路径 68 { 69 int Min = INF; 70 //通过反向弧 在源点到汇点的最少花费路径 找最小增广流 71 for(int v=pre[t];v!=-1;v=pre[TO[v^1]]) 72 Min = min(Min, CAP[v]-FLOW[v]); 73 //增广 74 for(int v=pre[t];v!=-1;v=pre[TO[v^1]]) 75 { 76 FLOW[v]+=Min; 77 FLOW[v^1]-=Min; 78 cost+=COST[v]*Min;//增广流的花销 79 } 80 flow+=Min;//总流量累加 81 } 82 } 83 int main(){ 84 int ca=0; 85 while(scanf("%d%d",&n,&m)!=EOF){ 86 init(); 87 for(int i=0,u,v,d,a;i<m;i++){ 88 scanf("%d%d%d%d",&u,&v,&d,&a); 89 addEdge(u,v,1,d); 90 addEdge(u,v,1,d+a); 91 } 92 S=0,T=n+1; 93 addEdge(S,1,2,0); 94 addEdge(n,T,2,0); 95 int cost,flow; 96 MCMF(S,T,cost,flow); 97 printf("Case %d: %d\n",++ca,cost);//最小费用 最大流 98 } 99 return 0; 100 }
H.Discrepancies in the Voters List回到顶部
题意
给n1,n2,n3个数,问出现>=2的数,n1,n2,n3<=50000
题解
map哈希求次数>=2,复杂度O(sumnlog(sumn))
PS:题意也没说会不会出现相同的,没有相同的话上面这么做
PS:如果有相同的话,处理map<int,int>f[3]判断是否都有
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 map<int,int>ma; 5 int main(){ 6 int n1,n2,n3,x; 7 scanf("%d%d%d",&n1,&n2,&n3); 8 for(int i=1;i<=n1;i++)scanf("%d",&x),ma[x]++; 9 for(int i=1;i<=n2;i++)scanf("%d",&x),ma[x]++; 10 for(int i=1;i<=n3;i++)scanf("%d",&x),ma[x]++; 11 int cnt=0; 12 for(auto X:ma)if(X.second>=2)cnt++; 13 printf("%d\n",cnt); 14 for(auto X:ma)if(X.second>=2)printf("%d\n",X.first); 15 return 0; 16 }
I.Optimal Programs回到顶部
题意
求一个操作序列,ADD,SUB,MUL,DIV分别是从栈顶取出两个再做对应操作,DUP是复制栈顶元素
f(x)=y代表栈初始值x,经过操作序列操作后,得到最后的值为y,要求对于每个x[i]都可以推出y[i]
n<=10,答案操作序列<=10
题解
最暴力的做法是5^10*10*10肯定不行,需要优化,这里优化的东西挺多的
优化1:如果当前序列存在除0,栈顶弹不出2个,这个序列就不用搜索下去了
优化2:如果最后栈的大小不为1,f(x[i])!=y[i]可以快速判断非法,可以继续搜索
优化3:如果所有x[i]=y[i]那么直接输出Empty sequence
优化4:初始只有一个x[i],那么第一次操作一定为DUP
复杂度O(<<5^9*10*10)
PS:又是一道特别难理解的题,淦
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 //0 ADD 5 //1 DUP 6 //2 DIV 7 //3 MUL 8 //4 SUB 9 int f,path[10],x[10],y[10],n; 10 char out[5][5]={"ADD","DIV","DUP","MUL","SUB"}; 11 struct node{ 12 int path[10]; 13 int p; 14 }; 15 //check=0序列不合法,1序列合法答案不合法,2序列合法答案合法 16 int check(node v){ 17 //for(int i=0;i<=v.p;i++)printf("%d ",v.path[i]); 18 //puts(""); 19 for(int i=0;i<n;i++){ 20 stack<int>st; 21 st.push(x[i]); 22 int a,b; 23 for(int j=0;j<=v.p;j++){ 24 if(v.path[j]==0){ 25 if(!st.empty())a=st.top(),st.pop(); 26 else return 0; 27 if(!st.empty())b=st.top(),st.pop(); 28 else return 0; 29 if(a+b<-30000||a+b>30000)return 0; 30 else st.push(a+b); 31 }else if(v.path[j]==1){ 32 if(!st.empty())a=st.top(),st.pop(); 33 else return 0; 34 if(!st.empty())b=st.top(),st.pop(); 35 else return 0; 36 if(a==0)return 0; 37 else if(b/a<-30000||b/a>30000)return 0; 38 else st.push(b/a); 39 }else if(v.path[j]==2){ 40 if(!st.empty())a=st.top(); 41 else return 0; 42 st.push(a); 43 }else if(v.path[j]==3){ 44 if(!st.empty())a=st.top(),st.pop(); 45 else return 0; 46 if(!st.empty())b=st.top(),st.pop(); 47 else return 0; 48 if(a*b<-30000||a*b>30000)return 0; 49 else st.push(a*b); 50 }else if(v.path[j]==4){ 51 if(!st.empty())a=st.top(),st.pop(); 52 else return 0; 53 if(!st.empty())b=st.top(),st.pop(); 54 else return 0; 55 if(b-a<-30000||b-a>30000)return 0; 56 else st.push(b-a); 57 } 58 } 59 if(st.size()!=1)return 1; 60 else if(st.top()!=y[i])return 1; 61 } 62 return 2; 63 } 64 void bfs(){ 65 queue<node>q; 66 node s; 67 s.path[0]=2;s.p=0; 68 q.push(s); 69 while(!q.empty()){ 70 node u=q.front();q.pop(); 71 for(int i=0;i<=4;i++){ 72 if(u.p+1<=9){ 73 node v=u; 74 v.path[u.p+1]=i;v.p++; 75 int oo=check(v); 76 if(oo==1)q.push(v); 77 else if(oo==2){ 78 for(int j=0;j<=v.p;j++)path[j]=v.path[j]; 79 f=v.p;return; 80 } 81 } 82 } 83 } 84 } 85 int main(){ 86 int ca=1; 87 while(scanf("%d",&n)!=EOF,n){ 88 for(int i=0;i<n;i++)scanf("%d",&x[i]); 89 for(int i=0;i<n;i++)scanf("%d",&y[i]); 90 int same=1; 91 for(int i=0;i<n;i++)if(x[i]!=y[i])same=0; 92 //5^10=9e6 93 printf("Program %d\n",ca++); 94 if(same){ 95 printf("Empty sequence\n\n"); 96 continue; 97 } 98 f=-1; 99 bfs(); 100 if(f>-1){ 101 printf("%s",out[path[0]]); 102 for(int i=1;i<=f;i++) 103 printf(" %s",out[path[i]]); 104 printf("\n"); 105 } 106 else printf("Impossible\n"); 107 puts(""); 108 } 109 return 0; 110 }
J.Bovine Bridge Battle回到顶部
题意
n个点,问多少个4元组两两对称,n<=1000
题解
求出每两个点的中点,统计次数,为了防止出现小数,可以先*2
然后遍历所有中点,可以发现就是C(n,2),复杂度O(2n^2logn)
PS:可以把pair<int,int>hash成一个long,然后存在unordered中可以少个log,复杂度O(n^2)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define LL long long 5 6 const int N=1005; 7 8 int x[N],y[N]; 9 map<pair<int,int>,int>ma; 10 11 int main(){ 12 int n; 13 scanf("%d",&n); 14 for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]); 15 for(int i=1;i<=n;i++)x[i]<<=1,y[i]<<=1; 16 for(int i=1;i<=n;i++)for(int j=1;j<i;j++){ 17 int x1=(x[i]+x[j])/2; 18 int y1=(y[i]+y[j])/2; 19 ma[{x1,y1}]++; 20 } 21 LL ans=0; 22 for(auto X:ma)ans+=(1LL*X.second*(X.second-1))/2; 23 printf("%lld",ans); 24 return 0; 25 }