acwing暑假训练题解(Week5&6)

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值