5385: 树的遍历
Total Submit: 22 Accepted:16
Description
给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。
Input
输入第一行给出一个正整数N(<=30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。
Output
在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
Sample Input
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
Sample Output
4 1 6 3 5 7 2
如题意所述,我们可以直接得到其前序遍历结果,所以hash一下就好了,特别大了而且成链就写一个大数吧,hash一下就好的
#include <bits/stdc++.h> using namespace std; map<int,int>M; int s[31],c[31],l; void la(int l,int r,int st,int ed,int f) { if(l<=r&&st<=ed) { M[f]=c[ed]; for(int i=l; i<=r; i++) if(c[ed]==s[i]) { la(l,i-1,st,st+i-1-l,2*f+1),la(i+1,r,st+i-l,ed-1,2*f+2); return ; } } } int main() { cin>>l; for(int i=0;i<l;i++)cin>>c[i]; for(int i=0;i<l;i++)cin>>s[i]; la(0,l-1,0,l-1,0); int f=0; for(auto X:M) { if(f)cout<<" "; cout<<X.second,f=1; } return 0; }
taozi的队列代码
#include<bits/stdc++.h> using namespace std; int post[1000],in[1000],Left[1000],Right[1000]; int n; int build(int L1,int R1,int L2,int R2) { if(L1>R1)return 0; int root=post[R1]; int pos=L2; while(in[pos]!=root)pos++; int cnt=pos-L2; Left[root]=build(L1,L1+cnt-1,L2,pos-1); Right[root]=build(L1+cnt,R1-1,pos+1,R2); return root; } void level() { queue<int>q; q.push(post[n]); int f=0; while(!q.empty()) { int u=q.front();q.pop(); if(!f)printf("%d",u),f=1; else printf(" %d",u); if(Left[u])q.push(Left[u]); if(Right[u])q.push(Right[u]); } } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>post[i]; for(int i=1;i<=n;i++) cin>>in[i]; build(1,n,1,n); level(); return 0; }
5445: 中位数
总提交: 15 测试通过:2
描述
给定两个序列,都已经从小到大排序,求两个序列合并后的中位数。
所谓中位数是指:当排序后的序列元素个数是奇数时取中间值,否则去中间两个数的平均数。
你能想出O(log(m+n))复杂度的算法吗?
输入
多组数据。每组数据的:
第一行为两个整数n和m,(1<=n, m<=10000000)。
第二行由n个从小到大排序的整数。
第三行由m个从小到大排序的整数。
以EOF结束。
输出
输出中位数,保留2位小数。
样例输入
2 1
1 3
2
2 2
1 2
3 4
样例输出
提示
因本题输入数据规模较大,请使用类似以下代码输入一个整数(输入挂):
int Scan()
{
int res = 0, ch, flag = 0;
if((ch = getchar()) == '-') //判断正负
flag = 1;
else if(ch >= '0' && ch <= '9') //得到完整的数
res = ch - '0';
while((ch = getchar()) >= '0' && ch <= '9' )
res = res * 10 + ch - '0';
return flag ? -res : res;
}
枚举pos看他是第几个数,当然也会相等,所以这个还是特判一下的
为什么这样是可以的呢,因为你每个数在两个数组均会有一个位置的,我只要遍历每个数组就可以找到
mi代表当前位置,其实个数是mi+1
pos-1代表可以插入到的位置,前一个可能是相等,也可能是不等
#include<bits/stdc++.h> using namespace std; const int N=10000005; int a[N],b[N],n,m; long long L,R,zws; int la() { while(L<=R) { int mi=(L+R)>>1; int pos=upper_bound(a,a+n,b[mi])-a; if(pos&&a[pos-1]==b[mi]) { if(mi+pos==zws)return mi; } if(mi+pos+1<zws) { if(mi==m-1)return -1; L=mi+1; } else if(mi+pos+1>zws) { if(mi==0)return -1; R=mi-1; } else { return mi; } } return -1; } int lb() { while(L<=R) { int mi=(L+R)>>1; int pos=upper_bound(b,b+m,a[mi])-b; if(pos&&b[pos-1]==a[mi]) { if(mi+pos==zws) return mi; } if(mi+pos+1<zws) { if(mi==n-1)return -1; L=mi+1; } else if(mi+pos+1>zws) { if(mi==0)return -1; R=mi-1; } else { return mi; } } return -1; } int Scan() { int res = 0, ch, flag = 0; if((ch = getchar()) == '-') flag = 1; else if(ch >= '0' && ch <= '9') res = ch - '0'; while((ch = getchar()) >= '0' && ch <= '9' ) res = res * 10 + ch - '0'; return flag ? -res : res; } int main() { while(~scanf("%d%d",&n,&m)) { for(int i=0; i<n; i++)a[i]=Scan(); for(int i=0; i<m; i++)b[i]=Scan(); L=0,R=m-1,zws=(n+m+1)/2; int t=la(); if(t==-1) L=0,R=n-1,t=lb(),t=a[t]; else t=b[t]; if((n+m)%2==0) { zws++; L=0,R=m-1; int t1=la(); if(t1==-1) L=0,R=n-1,t1=lb(),t1=a[t1]; else t1=b[t1]; t+=t1; } else t+=t; printf("%.2f ",t/2.); } return 0; }
5201: 数字游戏
Total Submit: 125 Accepted:17
Description
栋栋正在和同学们玩一个数字游戏。
游戏的规则是这样的:栋栋和同学们一共n个人围坐在一圈。栋栋首先说出数字1。接下来,坐在栋栋左手边的同学要说下一个数字2。再下面的一个同学要从上一个同学说的数字往下数两个数说出来,也就是说4。下一个同学要往下数三个数,说7。依次类推。
为了使数字不至于太大,栋栋和同学们约定,当在心中数到 k-1 时,下一个数字从0开始数。例如,当k=13时,栋栋和同学们报出的前几个数依次为:
1, 2, 4, 7, 11, 3, 9, 3, 11, 7。
游戏进行了一会儿,栋栋想知道,到目前为止,他所有说出的数字的总和是多少。
Input
输入的第一行包含三个整数 n,k,T,其中 n 和 k 的意义如上面所述,T 表示到目前为止栋栋一共说出的数字个数。
Output
输出一行,包含一个整数,表示栋栋说出所有数的和。
Sample Input
Sample Output
3 13 3
Hint
17
样例说明
栋栋说出的数依次为1, 7, 9,和为17。
数据规模和约定
1 < n,k,T < 1,000,000;
可以枚举要加的值
#include<bits/stdc++.h> using namespace std; int main() { long long n,k,T,res=0,last=1; cin>>n>>k>>T; for(int i=1;i<T;i++) { res+=(last+(2*i*n-n+1)*n/2)%k; last=(last+(2*i*n-n+1)*n/2)%k; } cout<<res+1; return 0; }
5202: 网络寻路
总提交: 1 测试通过:1
描述
X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包,为了安全起见,必须恰好被转发两次到达目的地。该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径。
源地址和目标地址可以相同,但中间节点必须不同。
如下图所示的网络。
1 -> 2 -> 3 -> 1 是允许的
1 -> 2 -> 1 -> 2 或者 1 -> 2 -> 3 -> 2 都是非法的。
输入
输入数据的第一行为两个整数N M,分别表示节点个数和连接线路的条数(1<=N<=10000; 0<=M<=100000)。
接下去有M行,每行为两个整数 u 和 v,表示节点u 和 v 联通(1<=u,v<=N , u!=v)。
输入数据保证任意两点最多只有一条边连接,并且没有自己连自己的边,即不存在重边和自环。
输出
输出一个整数,表示满足要求的路径条数。
样例输入
3 3
1 2
2 3
1 3
样例输出
6
提示
样例输入2
4 4
1 2
2 3
3 1
1 4
样例输出2
10
可以dfs去枚举,也就是去枚举两边的点让他们不形成环
但是注意目的地可以和源地址相同,那么合法的就有以下两种情况
case1
case2
和当前节点的度有关,即这条边除了这条路带来的度的乘积
#include<bits/stdc++.h> using namespace std; const int N=100005; int d[N],u[N],v[N],n,m; int main() { long long ans=0; scanf("%d%d",&n,&m); for(int i=0; i<m; i++)scanf("%d%d",&u[i],&v[i]),d[u[i]]++,d[v[i]]++; for(int i=0; i<m; i++)if(d[u[i]]>1&&d[v[i]]>1)ans+=(d[u[i]]-1)*1LL*(d[v[i]]-1)*2; printf("%I64d ",ans); return 0; }
5204: 小朋友排队
Total Submit: 18 Accepted:2
Description
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
Input
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
Output
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
Sample Input
3
3 2 1
Sample Output
9
Hint
样例说明
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
数据规模和约定
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
前置技能逆序对,即交换相邻两个数字的最小次数
再考虑这个问题,就是问你一个数要交换几次,这个自己可以推一下
其实就是把数倒过来正序对的个数比如 3 2 1逆序对贡献是0 1 2
倒过来1 2 3正序对贡献0 1 2,即0+2 1+1 2+0所求次数
他卡我其他求逆序对的方式,只能归并排序过的
#include <stdio.h> #include <bits/stdc++.h> using namespace std; const int N=100005; int w[N],sum[N],n; struct T { int x,num; } a[N],T[N]; void la(int l,int r) { if(r-l==1)return; int m=l+r>>1,tm=l+r>>1,tl=l,i=l; la(l,m),la(m,r); while(tl<m||tm<r) { if(tm>=r||(tl<m&&a[tl].x<=a[tm].x)) T[i++]=a[tl++],T[i-1].num+=tm-m; else T[i++]=a[tm++],T[i-1].num+=m-tl; } for(int i=l; i<r; i++)a[i]=T[i]; } int main() { scanf("%d",&n); for(int i=0; i<n; i++) scanf("%d",&a[i].x),a[i].num=0; la(0,n); __int64 ans=0; for(int i=0; i<n; i++)ans+=a[i].num*1LL*(a[i].num+1)/2; printf("%I64d",ans); return 0; }
5205: 最大子阵
Total Submit: 21 Accepted:8
Description
给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
Input
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
Output
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
Sample Input
3 3
-1 -4 3
3 4 -1
-5 -2 8
Sample Output
10
Hint
样例说明
取最后一列,和为10。
数据规模和约定
对于50%的数据,1<=n, m<=50;
对于100%的数据,1<=n, m<=500,A中每个元素的绝对值不超过5000。
想起来了要用最大字段和,但是这个dp过程还是不好想的
你可以对行或列做下前缀和,然后再求下最大字段和
基于行的dp
#include<stdio.h> const int N=505; int r[N][N]; int main() { int n,m,x; scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d",&x),r[i][j]=r[i-1][j]+x; int ma=x,t; for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) { t=0; for(int k=1; k<=m; k++) { t+=r[j][k]-r[i-1][k]; if(t>ma)ma=t; if(t<0)t=0; } } printf("%d",ma); return 0; }
数据分布不均匀,基于列的dp跑起来比较慢
#include<stdio.h> const int N=505; int r[N][N]; int main() { int n,m,x; scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d",&x),r[i][j]=r[i][j-1]+x; int ma=x,t; for(int i=1; i<=m; i++) for(int j=i; j<=m; j++) { t=0; for(int k=1; k<=n; k++) { t+=r[k][j]-r[k][i-1]; if(t>ma)ma=t; if(t<0)t=0; } } printf("%d",ma); return 0; }
5206: 约数倍数选卡片
Total Submit: 1 Accepted:1
Description
闲暇时,福尔摩斯和华生玩一个游戏:
在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
1,2,3, 6,12,18,24 ....
当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
Input
输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
Output
程序则输出必胜的招法!!
Sample Input
2 3 6
3 6
Sample Output
3
Hint
样例输入2
1 2 2 3 3 4 5
3 4 5
样例输出2
4
博弈题目,但是题目数据应该不是很多,直接dfs就过了
大概思路是这样的,因为我要选择必胜,即某个选择可以导致对手出现必败态
还要输出最小的数字,所以需要对b数组进行排序
然后需要预处理一下一个数的约数和倍数,假如这个数量级很大了,貌似就很难过了,倍数还有约数要很好的处理下,可以把有些数组直接赋值过去
#include<bits/stdc++.h> using namespace std; #define dbg(x) cout<<#x<<" = "<< (x)<< endl const int N=105; int num[N]; vector<int> b,f[N]; int dfs(int x) { for(int i=f[x].size()-1; i>=0; i--) { int pp=f[x][i],t; if(num[pp]) { num[pp]--,t=dfs(pp),num[pp]++; if(t)return 0; } } return 1; } void la() { for(auto X:b) { if(num[X]) { num[X]--; if(dfs(X)) { cout<<X; return; } num[X]++; } } cout<<-1; } int main() { int x; string s; getline(cin,s); stringstream ss(s); while(ss>>x)num[x]++; getline(cin,s); stringstream st(s); while(st>>x)b.push_back(x); sort(b.begin(),b.end()); for(int i=1; i<N ; i++) if(num[i]) for(int j=1; j<N ; j++) if(num[j]&&(i%j==0||j%i==0))f[i].push_back(j); la(); return 0; }
5200: 买不到的数目
Total Submit: 57 Accepted:43
Description
小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
Input
两个正整数,表示每种包装中糖的颗数(都不多于1000)
Output
一个正整数,表示最大不能买到的糖数。
如果这个数不存在,则输出-1。
Sample Input
4 7
Sample Output
17
Hint
样例输入2
3 5
样例输出2
7
题目不严谨,因为有1的时候是不存在这个数的,而且你很快会发现只有互质的时候才存在,否则是不存在的
但是是一个很好的数论
1. (x-1)(y-1)-1 不能被表示为 ax+by的形式
2. 大于等于(x-1)(y-1)都能被表示为 ax+by的形式
#include <stdio.h> int main() { int a,b; scanf("%d%d",&a,&b); printf("%d",a*b-a-b); }