Week5
数组延伸
输入样例:
2
1 2
12
4 2
4 6 8 2
输出样例:
36
44
思路:当q被展开时,他会多出x个整数q/x,这些整数的和也是q,也就是被展开一次,这个数就会被重复计算一次,那么我们就不难想到,我们需要计算的是每个数能够被展开多少次,如果计算不会中途停止的话这个数也就会被计算多少次,但是由于题目中说当出现不能整除的情况时会停止,所以我们需要关注的是所有数能够被展开的次数的最小值cnt,并从头开始遍历,当出现某一个数的展开次数等于cnt时就终止
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=100003;
int a[N],cnt[N];
int main()
{
int T;
cin>>T;
while(T--)
{
int n,x;
scanf("%d%d",&n,&x);
long long ans=0;
int mi=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ans+=a[i];
int t=a[i];
cnt[i]=0;//注意初始化
while(t%x==0)
{
t/=x;
cnt[i]++;
}
mi=min(mi,cnt[i]);
}
ans*=(mi+1);
for(int i=1;i<=n;i++)
{
if(cnt[i]>mi) ans+=a[i];
else break;
}
printf("%lld\n",ans);
}
return 0;
}
分水果
输入样例:
7
1 2 1
0 0 0
9 1 7
2 2 3
2 3 2
3 2 2
4 4 4
输出样例:
3
0
4
5
5
5
7
思路:因为每种水果最多只能获得一个且只有三种水果,总的状态数较少,所以我们可以选择暴力枚举其中状态选与不选,然后与所给水果进行比较看是否满足题意,如果满足题意,我们则与最大值进行比较看是否能够更新最大值
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
int t[7][3]={//记录每种小朋友分到的水果
{0,0,1},
{0,1,0},
{0,1,1},
{1,0,0},
{1,0,1},
{1,1,0},
{1,1,1},
};
int main()
{
int T,a,b,c;
cin>>T;
while(T--)
{
scanf("%d%d%d",&a,&b,&c);
int cnta,cntb,cntc,ans=0,cnt;//cnta/b/c分别为小朋友分到的三种水果数目
for(int i=1;i<1<<7;i++)//状态压缩来枚举每种奖励是否被选择
{
cnta=cntb=cntc=cnt=0;
for(int j=0;j<7;j++)
if(i>>j&1)
{
cnta+=t[j][0];
cntb+=t[j][1];
cntc+=t[j][2];
cnt++;
}
if(cnta<=a&&cntb<=b&&cntc<=c)
ans=max(ans,cnt);
}
printf("%d\n",ans);
}
return 0;
}
楼层
输入样例:
4
7 3
1 5
22 5
987 13
输出样例:
3
1
5
77
思路:题目比较简单,但是需要注意的是第一层需要特判一下,并且高于一层的是减3并不是减2,找两个数试一下就会发现规律
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int n,x;
cin>>n>>x;
if(n<=2)
{
cout<<"1"<<endl;
continue;
}
cout<<(n-3)/x+2<<endl;
}
return 0;
}
打印数字菱形
直接模拟即可,详情见代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
if(i+j>=n) printf("%d ",i+j-n);
else printf(" ");
for(int j=n-1;j>=0;j--)
if(i+j>=n) printf("%d ",i+j-n);
else printf(" ");
printf("\n");
}
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<=n;j++)
if(i+j>=n) printf("%d ",i+j-n);
else printf(" ");
for(int j=n-1;j>=0;j--)
if(i+j>=n) printf("%d ",i+j-n);
else printf(" ");
printf("\n");
}
return 0;
}
数组补全
输入样例1:
5 3 5 18 4
3 5 4
输出样例1:
4 1
输入样例2:
5 3 5 16 4
5 5 5
输出样例2:
-1
思路:我们先读入这k个数,再读入过程中我们记录小于y的数有多少个,不妨即为cnt,若cnt>n/2,则无论我们怎么加数,最后的中位数一定是小于y的,若cnt<n/2,这个时候我们肯定能够通过加数使得中位数不小于y,此时我们只需要满足总和不大于x即可,所以我们尽量添加一下较小的数,我们此时最多能够添加的小于y的数的个数为n/2-cnt,我们将这些数全设置为1,剩下的数只能填大于等于y的数了,我们也填最小的数,全填y,如果这样构造后的数组的和还大于x,那么肯定是没有满足题意的序列了,反之我们就按照这样的方法进行构造
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=1003;
int a[N];
int main()
{
int n,k,p,x,y;
cin>>n>>k>>p>>x>>y;
int sum=0,cnt=0;//cnt记录前k个数中数值小于y的数的个数
for(int i=1;i<=k;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
if(a[i]<y) cnt++;
}
if(cnt<=n/2)//此时中位数小于y
{
int t=n/2-cnt;//t为最多能够补充的小于y的数的个数
if(sum+t+(n-k-t)*y>x)//sum+t+(n-k-t)*y为可构造的满足题意的序列的最小值
{
puts("-1");
return 0;
}
for(int i=k+1;i<=k+t;i++)
a[i]=1;
for(int i=k+t+1;i<=n;i++)
a[i]=y;
for(int i=k+1;i<=n;i++)
printf("%d ",a[i]);
}
else//已经不可能满足中位数大于等于y这个条件了
{
puts("-1");
}
return 0;
}
Week6
数组重排
输入样例:
3
1
7
4
1 1 3 5
6
3 2 1 5 6 4
输出样例:
7
1 5 1 3
2 4 6 1 3 5
思路:由于坐标是依次增加的,那我们只要使得数组里面的数依次减少就可以使得对于任意 1≤i<j≤n,j−a[j]≠i−a[j] 均成立
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=103;
int a[N];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
printf("%d ",a[i]);;
puts("");
}
return 0;
}
素数矩阵
输入样例:
2
4
2
输出样例:
4 6 8 1
4 9 9 9
4 10 10 65
1 4 4 4
1 1
1 1
思路:这道题目是找规律的题目,我们只需要每行每列填2个1就可以了,构造方式随意
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j||j==i+1) printf("1 ");
else if(j==(i+1)%n) printf("1 ");
else printf("0 ");
}
puts("");
}
}
return 0;
}
移动序列
输入样例:
5
7
0 0 1 0 1 0 1
3
1 0 0
5
1 1 0 0 1
6
1 0 0 0 0 1
5
1 1 0 1 1
输出样例:
2
0
2
4
1
思路:我们直接对原序列进行模拟,从左往右每次遇到1就将其合并,直到所有的1连在一起
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=103;
int a[N];
int main()
{
int T,n;
cin>>T;
while(T--)
{
scanf("%d",&n);
int last=0;//记录上一个1出现的位置
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i])
{
if(last)
ans+=i-last-1;//每次合并最少移动次数
last=i;
}
}
printf("%d\n",ans);
}
return 0;
}
回文子序列
输入样例:
5
3
1 2 1
5
1 2 2 3 2
3
1 1 2
4
1 2 2 1
10
1 1 2 2 3 3 4 4 5 5
输出样例:
YES
YES
NO
YES
NO
思路:注意子序列是不要求连续的序列,我们用p[i]记录i第一次出现的位置,这样等到i再次出现时,我们只需要判断他们的间隔有没有大于等于2即可
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=5003;
int p[N];//p[i]记录i上一次出现的位置
int main()
{
int T;
cin>>T;
while(T--)
{
int n,t;
cin>>n;
memset(p,0,sizeof p);
bool flag=false;
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
if(!p[t]) p[t]=i;
if(i-p[t]>=2) flag=true;
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}