【题目记录】——2021“MINIEYE杯”中国大学生算法设计超级联赛(6)

这篇博客介绍了两个算法问题的解决方案。第一个是寻找包含给定数的最短区间,其区间和为素数,通过素数筛选实现。第二个问题是判断一组数字是否能按给定的中位数划分,通过分析数字段的组合可能性得出结论。这两个问题都涉及到数学思维和算法设计技巧。
摘要由CSDN通过智能技术生成


题目集地址 2021“MINIEYE杯”中国大学生算法设计超级联赛(6)
题目集原地址 2021“MINIEYE杯”中国大学生算法设计超级联赛(6)
这次我们做出了1001和1005。

1001 Yes, Prime Minister 思维,数学

题目地址1001 Yes, Prime Minister
题意:给一个数x,找到包含他的最短区间,保证区间和是素数,输出最短区间长度。
思路:参考博客[hdu] P7025 Yes, Prime Minister
AC代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const ll maxn=3e7+1;
const int leng = 7e7+5;//leng要比maxn大很多,不然筛素数的时候会re,我在这re了好几发。。。。

bool IsPrime[leng];//真值为素数
ll Prime[leng],ans,tot;
ll Prime2[leng],top;
void Choose(ll n) { //筛选到n
    memset(IsPrime,1,sizeof(IsPrime));//初始化
    //假设每个数为素数
    IsPrime[1]=IsPrime[0]=0;

    for(ll i=2; i<=n; i++) {
        if(IsPrime[i])//如果这个数没筛掉,那么将其加入素数序列
            Prime[++tot]=i;
        for(ll j=1; j<=tot&&i*Prime[j]<=n; j++) {
            IsPrime[i*Prime[j]]=0;
            if(!i%Prime[j])
                break;
        }
    }
    for (int i=1; i<=n; i++)//便于找最小的大于x的使2*x+1为素数的值
		if (IsPrime[2*i+1]) Prime2[++top]=i;
	return ;
}
int main() {
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0);
    Choose(maxn);//素数筛

    int T;
    scanf("%d",&T);
    int x;
    while(T--) {
        scanf("%d",&x);
        ans = INF;
        if(!x) {
            cout << 3 << endl;
        }
        else if(x > 0)
        {
            if(IsPrime[x])
            {
                cout << 1 << endl;
            }
            else if(IsPrime[2*x-1]||IsPrime[2*x+1])
            {
                cout << 2 << endl;
            }
            else
            {
                int pos = upper_bound(Prime + 1,Prime + tot + 1,x) - Prime;//找最小的大于x的质数
                ans = Prime[pos] * 2;
                pos = upper_bound(Prime2 + 1,Prime2 + top + 1,x) - Prime2;//找最小的大于2x+1的质数
                ans = min(ans, 2*Prime2[pos]+1);
                cout << ans << endl;
            }
        }
        else
        {
            x = -x;
            int pos = upper_bound(Prime + 1,Prime + tot + 1,x) - Prime;//找最小的大于x的质数
            ans = Prime[pos]*2;
            pos = upper_bound(Prime2 + 1,Prime2 + top + 1,x) - Prime2;//找最小的x使2*x+1是质数
            ans = min(ans,Prime2[pos]*2+1);
            cout << ans << endl;
        }
    }
    return 0;
}

1005 Median 思维

题目地址1005 Median
题意:给n个数1-n,分为m组,每组给定一个中位数,问有没有可能满足给定的中位数。
思路:首先我们把被m个中位数分割开的数字分成一段一段的:假设 n = 6 , m = 2 , b 1 = 3 , b 2 = 5 n = 6, m = 2, b1 = 3, b2 =5 n=6,m=2,b1=3,b2=5,那么 1 , 2 , ⋅ ⋅ ⋅ , n 1, 2, · · · , n 1,2,,n 这些数会被 { 1 , 2 } { 4 } { 6 } \{ 1,2\}\{ 4\} \{ 6\} {1,2}{4}{6}三段,然后我们会发现任意两段中的任意一对数字一定可以配对消掉
如果长度最大的段的数字个数小于等于其它段的数字个数之和,那么最终要么全部消掉,要么剩下一个,且剩下的这个数可以在任何一段内。如果会剩下,可以将最后一段的数字剩下最后一个(使得这个剩下的数数值最大),此时再把最后一段的数字放到中位数最小的集合中即可满足题意,所以答案一定为 YES。
如果长度最大的段的数字个数大于其它段的数字个数之和,那么最终剩下的所有数字都在最大的这段内。设中位数小于这一段的最小值的集合的个数为 x,容易发现当且仅当 x不小于这一段剩下的数字个数时有解。
我们当时总想着用什么数据结构去解决这个问题了,结果是要自己找规律的。

/*
** Author:skj
** Time:2021-8-5
** function:1005
*/
#include <bits/stdc++.h>
const int maxn = 1e5+5;
using namespace std;
int a[maxn];

void solve()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int len = 0,num = 0;//最长段的长度和最长段的最小的数,比最小的数小的中位数个数
    for(int i = 1;i <= m;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a+1,a+m+1);
    if(a[1] > 1)
    {
        len = a[1] - 1;
    }
    for(int i = 1;i<m;i++)
    {
        if(len < a[i+1]-a[i]-1)
        {
            len = a[i+1]-a[i]-1;
            num = i;
        }
    }
    if(a[m] < n)
    {
        if(len < n - a[m])
        {
            len = n - a[m];
            num = m;
        }
    }
    if(len <= n-m-len)
    {
        printf("YES\n");
    }
    else
    {
        if(num >= 2*len+m-n)
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
    }
}
int main()
{
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int t;
	cin >> t;
	while(t--)
    {
        solve();
        memset(a,0,sizeof(a));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值