骰子
题目描述
你有n颗六面骰子,每个面上有一个数字,你随机地抛掷这n颗骰子,求各骰子顶面数字和的出现概率。
比如你有2颗骰子,那么它们的六面的数都是1∼6,那么出现的数字和是2∼12,其出现的概率依次为
输入格式
第一行是一个整数T (1≤T≤50),表示测试样例的个数。
以后每个样例的第一行是一个整数n(1≤n≤8),表示骰子的个数。 以后的n行,每行6个整数ai (1≤ai≤1024),表示第i个骰子六面上的数字。
输出格式
依次输出每个样例。
先输出会出现多少种数字;然后每行按出现数字和,从小到大输出,一个数字输出1行,格式如”n: x/y”,其中n为出现的数字,x/y为出现的概率分数,保证x,y互质。英文冒号后有一个空格。
样例输入
2
1
1 1 2 2 3 3
2
1 2 3 4 5 6
1 2 3 4 5 6
样例输出
3
1: 1/3
2: 1/3
3: 1/3
11
2: 1/36
3: 1/18
4: 1/12
5: 1/9
6: 5/36
7: 1/6
8: 5/36
9: 1/9
10: 1/12
11: 1/18
12: 1/36
心得与代码
这次整了个活,直接整了个八层嵌套循环,使用map输出就不用排序啦
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <map>
typedef __int64 ll;
using namespace std;
ll TZ[10][10];
ll fun(ll a,ll b){
ll t; //求最大公约数
if(a<b){
t=a;
a=b;
b=t;
}
while(b!=0){
t=a%b;
a=b;
b=t;
}
return a;
}
int main(){
ll N,k,n,sum,sum1,sum2,sum3,sum4,sum5,sum6,sum7;
ll i,j,t,o,p,q,r,s;
ll cnt,ans;
map<ll,ll>::iterator iter;
scanf("%I64d",&N);
while(N--){
map<ll,ll> map;
scanf("%I64d\n",&n);
memset(TZ,0,sizeof(TZ));
for(i=0;i<n;i++){
for(j=0;j<6;j++){
scanf("%I64d",&t);
TZ[i][j]=t;
}
}
if(n==1){
for(i=0;i<6;i++){
sum=TZ[0][i];
map[sum]++;
}
}
else if(n==2){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j]+sum;
map[sum1]++;
}
}
}
else if(n==3){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j];
for(t=0;t<6;t++){
sum2=TZ[2][t]+sum1+sum;
map[sum2]++;
}
}
}
}
else if(n==4){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j];
for(t=0;t<6;t++){
sum2=TZ[2][t];
for(o=0;o<6;o++){
sum3=TZ[3][o]+sum+sum1+sum2;
map[sum3]++;
}
}
}
}
}
else if(n==5){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j];
for(t=0;t<6;t++){
sum2=TZ[2][t];
for(o=0;o<6;o++){
sum3=TZ[3][o];
for(p=0;p<6;p++){
sum4=TZ[4][p]+sum+sum1+sum2+sum3;
map[sum4]++;
}
}
}
}
}
}
else if(n==6){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j];
for(t=0;t<6;t++){
sum2=TZ[2][t];
for(o=0;o<6;o++){
sum3=TZ[3][o];
for(p=0;p<6;p++){
sum4=TZ[4][p];
for(q=0;q<6;q++){
sum5=TZ[5][q]+sum+sum1+sum2+sum3+sum4;
map[sum5]++;
}
}
}
}
}
}
}
else if(n==7){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j];
for(t=0;t<6;t++){
sum2=TZ[2][t];
for(o=0;o<6;o++){
sum3=TZ[3][o];
for(p=0;p<6;p++){
sum4=TZ[4][p];
for(q=0;q<6;q++){
sum5=TZ[5][q];
for(r=0;r<6;r++){
sum6=TZ[6][r]+sum+sum1+sum2+sum3+sum4+sum5;
map[sum6]++;
}
}
}
}
}
}
}
}
else if(n==8){
for(i=0;i<6;i++){
sum=TZ[0][i];
for(j=0;j<6;j++){
sum1=TZ[1][j];
for(t=0;t<6;t++){
sum2=TZ[2][t];
for(o=0;o<6;o++){
sum3=TZ[3][o];
for(p=0;p<6;p++){
sum4=TZ[4][p];
for(q=0;q<6;q++){
sum5=TZ[5][q];
for(r=0;r<6;r++){
sum6=TZ[6][r];
for(s=0;s<6;s++){
sum7=TZ[7][s]+sum+sum1+sum2+sum3+sum4+sum5+sum6;
map[sum7]++;
}
}
}
}
}
}
}
}
}
cnt=0;
ans=0;
for( iter=map.begin();iter!=map.end();iter++){
t=iter->second;
if(t!=0) {
cnt++;
ans+=t;
}
}
printf("%I64d\n",cnt);
for( iter=map.begin();iter!=map.end();iter++){
t=iter->second;
if(t!=0) {
s=iter->first;
printf("%I64d: ",s);
r=fun(t,ans);
printf("%I64d/%I64d\n",t/r,ans/r);
}
}
}
}
字母计数
题目描述
为了压缩一个只含小写英文字母的字符串,我们使用下面的方式表示它
- 任一字母c是表达式
- 任一字母c后接一个整数n也是一个表达式,表示把字母c重复n次,n是一个没有前导零的10进制整数,且 n≥2。
- 如果s1,s2是表达式,那么s1s2也是表达式。
- S是一个表达式,那么(S)n也是表达式,表示将S这个表达式表示的字符串重复n次,n是一个没有前导零的10进制整数,且 n≥2。
比如表达式 ((a2b)2b)2a 表示字符串aabaabbaabaabba。
现在给你一个表达式,请统计一下其中各个字符出现的次数。
输入
存在多组样例,每样一个样例,为一个字符串。 字符串只含小写英文字母,数字和括号,其长度不超过50个字符,输入数据保证符合表达式要求,且展开后的字符出现次数最高不超过109。
输出
依次输出每个样例的结果。 每个样例按照字母顺序,每个出现的字母输出一行。格式为"c : n",其中c为字母,n为出现的次数。 每个样例之后输出一个空行。
样例输入
((a2b)2b)2a
a2c3
样例输出
a : 9
b : 6
a : 2
c : 3
心得与代码
基本上就是照着题解思路写了,不过在写的时候还是遇到挺多问题的,比如宏观变量与局部变量的关系
后面和别的大佬讨论了下,还可以有从后面往前面读取的思路,也是一种挺妙的解法
思路:
dfs(s,e,c):
如果⾸字⺟是'(': // S -> (S)nS 形式
找到匹配的右括号位置为i。
dfs(s+1,i-1,c1)
查找i后的数字串,数字串的结束位置为j。
将其转换成数字t,并乘⼊数字c1。
否则: // S -> cnS 或者 s -> cS 形式
找到s+1开始的数字串,数字串结束位置为j。
如果存在数字串,将其转换成数字t,否则t=1。
c1中的s位置字符的数量为t,其他为0。
dfs(j+1,e,c2)
将c1和c2合并成c
#include <stdio.h>
#include <algorithm>
#include <string.h>
typedef __int64 ll;
using namespace std;
char str[100];
ll c3[30];
void dfs(int s,int e,ll c[]){
ll c1[30],c2[30];
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
int i,j,t,o,l=1,r=0;
ll sum=0;
if(str[s]=='('){
l=1;
r=0;
for(i=s+1;i<=e;i++){
if(str[i]=='(') l++;
if(str[i]==')'){
r++;
if(r==l) break;
}
}
dfs(s+1,i-1,c1);
// ll sum=0;
for(t=i+1;t<=e;t++){
if(str[t]>='0'&&str[t]<='9'){
sum+=str[t]-48;
sum=sum*10;
}
else break;
}
j=t-1;
sum=sum/10;
for(o=0;o<=26;o++){
c1[o]*=sum;
}
}
else {
for(i=s+1;i<=e;i++){
if(str[i]>='0'&&str[i]<='9') continue;
else break;
}
j=i-1;
if(i==s+1) sum=1;
else{
sum=0;
for(o=s+1;o<=j;o++){
sum+=str[o]-48;
sum=sum*10;
}
sum=sum/10;
}
c1[str[s]-'a']+=sum;
}
if(j+1<=e)dfs(j+1,e,c2);
for(o=0;o<=26;o++){
c[o]=c1[o]+c2[o];
}
for(o=0;o<=26;o++){
c3[o]=c[o];
}
}
int main(){
ll i,j,cnt;
memset(str,0,sizeof(str));
memset(c3,0,sizeof(c3));
while(scanf("%s",str)!=EOF){
cnt=0;
for(i=0;str[i]!='\0';i++){
cnt++;
}
dfs(0,cnt-1,c3);
for(i=0;i<=26;i++){
if(c3[i]!=0){
char ch=i+97;
printf("%c : %I64d\n",ch,c3[i]);
}
}
printf("\n");
memset(str,0,sizeof(str));
memset(c3,0,sizeof(c3));
}
}
众数
题目描述
众数是指多值集中出现次数最多的数。你每次可以将集合内的某个数加1,请问最多k次操作,你能获得众数最多的出现次数是多少?
比如集合S={1,3,3,4,5},k=2时,我们可以将2个3分别加1,得到新的集合S′={1,4,4,4,5},S′的众数为4,出现次数是3。
输入格式
第一行是一个整数T (1≤T≤20),表示样例的个数。
每个样例的第一行是两个整数n (1≤n≤105),表示多值集的大小;k (0≤k≤109),表示操作的次数上限。
第二行是n个整数,所有整数处于[1,109]区间内。
输出格式
按顺序每行输出一个样例的结果,为一个整数。
样例输入
3
1 0
1
2 1
1 2
3 1
1 2 2
样例输出
1
2
3
心得与代码
注意题目给的是一个集合,集合中的元素是非降排列的
#include <stdio.h>
//#include <unordered_map>
#include <algorithm>
#include <string.h>
typedef __int64 ll;
using namespace std;
int main(){
ll N;
ll num[100005],sum[100005];
// unordered_map<ll,ll>::iterator iter,it,vit;
scanf("%I64d",&N);
while(N--){
// unordered_map<ll,ll> map;
ll n,i,k,m,j;
ll t,cnt,maxnum,maxcnt,sum1;
scanf("%I64d %I64d",&n,&k);
for(i=0;i<n;i++){
scanf("%I64d",&t);
num[i]=t;
}
sort(num,num+n);
sum[0]=num[0];
for(i=1;i<n;i++){ //处理前缀和
sum[i]=sum[i-1]+num[i];
}
m=1;
for(i=0;num[i+m-1]!='\0';){
maxnum=num[i+m-1];
if(i==0) sum1=sum[i+m-1];
else sum1=sum[i+m-1]-sum[i-1];
if(k>=maxnum*m-sum1){
maxcnt=m;
m++;
}
else{
i++;
}
}
printf("%I64d\n",maxcnt);
memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
}
}
按位与
题目描述
一个正整数的数列A={a1,a2,⋯,an},取区间[l,r],如果我们在al,al+1,⋯,ar中最多去掉k个数,使得这些数按位与的结果是非0,那么我们称这样的区间是合法的。 请求最长的合法区间长度。
按位与,记为&,即将两个数按二进制对位做与运算,比如1(2)=01,2(2)=10, 所以1&2=01(2)&10(2)=00(2)=0。
输入格式
第一行是一个整数T(1≤T≤100),表示样例的个数。
每个样例的第一行是两个整数n(1≤n≤10000),k(0≤k≤n)。 第二行是n个正整数ai,1≤ai≤109。
输出格式
依次每行输出一个样例的结果,未一个整数。
样例输入
2
4 0
1 2 3 4
4 1
1 2 3 4
样例输出
2
3
心得与代码
思路:将十进制整数转为二进制,将转化后的二进制数保存在数组里,并按位对齐,高位填0,问题转化为求最长的一段区间 ,区间内最多有k个0 。由于转化后的二进制最多32位,所以只要求32次,取32次中最长的一段区间就行
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
typedef __int64 ll;
using namespace std;
char num2[10005][35];
char num3[10005][35];
ll num[10005];
int main(){
ll N,k,n;
ll i,j,t;
scanf("%I64d",&N);
while(N--){
scanf("%I64d%I64d",&n,&k);
memset(num,0,sizeof(num));
memset(num2,'\0',sizeof(num2));
for(i=0;i<n;i++){
scanf("%I64d",&t);
num[i]=t;
}
// for(i=0;i<n;i++){
// printf("%s\n",num2[i]);
// }
for(i=0;i<n;i++){//转二进制
itoa(num[i],num2[i],2);
// printf("%s\n",num2[i]);
}
memset(num3,'0',sizeof(num3));
for(j=0;j<n;j++){//按位对齐
t=0;
for(i=34;i>=0;i--){
if(num2[j][i]!='\0'){
num3[j][t]=num2[j][i];
t++;
}
}
}
// for(j=0;j<n;j++){
// for(i=0;i<34;i++){
// printf("%c ",num3[j][i]);
// }
// printf("\n");
// }
ll l=0,r=0,cnt,ans=0,maxans=0;
for(i=0;i<=32;i++){
cnt=0;
ans=0;
l=0;
r=0;
while(r<n) {
if(num3[r][i] == '0') {
cnt++;
}
while(cnt>k) {
if(num3[l][i] == '0') {
cnt--;
}
l++;
}
if(ans<(r-l+1)) ans=r-l+1;
r++;
}
if(ans>maxans) maxans=ans;
}
printf("%I64d\n",maxans);
}
}
图像
题目描述
一个2n×2n的图像,对它的像素点进行扫描,扫描的规则如下:
- 如果n=0,那么扫描这个像素点。
- 否则,将图片划分成4个2n−1×2n−1的子图片,按从上到下,从左到右的顺序依次扫描四个子图片。
现在请求第k(从1开始)个扫描的像素点对应的行与列?
输入格式
第一行是一个整数T (1≤T≤10000),表示样例的个数。
每个样例占一行,是一个整数k (1≤k≤260)。
输出格式
每个样例的每个查询输出一行,为两个整数,即扫描像素点的行与列(从1开始)。
样例输入
5
2
3
5
7
11
样例输出
1 2
2 1
1 3
2 3
4 1
样例解释
1 2 5 6
3 4 7 8
9 10 13 14
11 12 15 16
心得与代码
将图像分为四个子图,判断k位于哪个子图,将返回坐标定位到子图左上角,并将k减去前面子图的像素数
#include <stdio.h>
//#include <unordered_map>
#include <algorithm>
#include <string.h>
#include <math.h>
typedef __int64 ll;
using namespace std;
ll x,y; //x表示行,y表示列
void TX(ll n,ll k){
ll i,j,w;
if(n!=0){
j=pow(2,2*(n-1));
w=pow(2,n-1);
// printf("j=%I64d\n",j);
for(i=1;i<=4;i++){
if(i*j>=k) break;
}
k-=(i-1)*j;
// printf("k=%I64d\n",k);
if(i==1){
}
else if(i==2){
y=y+w;
}
else if(i==3){
x=x+w;
}
else if(i==4){
x=x+w;
y=y+w;
}
TX(n-1,k);
}
}
int main(){
ll N,k,n;
ll i,j;
scanf("%I64d",&N);
while(N--){
x=1;
y=1;
scanf("%I64d",&k);
for(i=0;i<=60;i++){
j=pow(2,2*i);
if(j>=k)
{
n=i;
break;
}
}
// printf("n=%I64d\n",n);
TX(n,k);
printf("%I64d %I64d\n",x,y);
}
}
最大的数
题目描述
一个无前导0的十进制正整数n,如果相邻两个数码不是同奇偶的,那么你可以交换它们。 只要存在这样的情况,你可以无限次数地进行这样的交换。请问你都能得到最大的十进制整数是多少?
比如136014→163014→613014→613104。
输入格式
存在多样例,数量不超过100。每行一个样例,为一个正整数n (1≤n≤1010000)。
输出格式
每行输出一个样例的结果,为一个整数。
样例输入
136014
1314
1357
样例输出
613104
4131
1357
心得与代码
分奇偶保存数字,然后从开头到结尾进行大小比较
//#include<iostream>
//#include <vector>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
int main(){
// ios::sync_with_stdio(false);//加快cin,cout的速度
char num[100005];
char num1[100005];
char num2[100005];
char num3[100005];
while(scanf("%s",num)!=EOF){
int i,j,t,flag=0,k,l,b;
// char maxnum,minnum;
l=num[0]%2;
j=0;
k=0;
for(i=0;num[i]!='\0';i++){ //将数字分奇偶保存
t=num[i]%2;
if(t!=l) {
num1[k]=num[i];
k++;
}
else{
num2[j]=num[i];
j++;
}
}
// printf("%s\n",num1);
// printf("%s\n",num2);
k=0;
t=0;
for(j=0,i=0;num2[j]!='\0'&&num1[i]!='\0';){
if(num1[i]>num2[j]){
num3[t]=num1[i];
// k=j+1;
t++;
i++;
flag=1;
// break;
}
else{
num3[t]=num2[j];
j++;
t++;
flag=1;
}
}
if(num2[j]!='\0') {
for(b=j;num2[b]!='\0';b++){
num3[t]=num2[b];
t++;
}
}
if(num1[i]!='\0') {
for(b=i;num1[b]!='\0';b++){
num3[t]=num1[b];
t++;
}
}
printf("%s\n",num3);
memset(num,0,sizeof(num));
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
memset(num3,0,sizeof(num3));
}
}