8月18日测试题解

一、细胞分裂

【题解】均分的本质是A整除B,A整除B等价于A的质因数是B的子集

1.将m1分解质因数,即m1=p1^a1*p2^a2*...*pk^ak

所以M=m1^m2=p1^(a1*m2)*p2^(a2*m2)*...*pk^(ak*m2)

2.如果s[i](细胞初始个数)不能被M分解出来的质因数(即p1,p2...pn)中的某一个整除的话,这种细胞就永远不可能装入M个瓶子中

换句话说,如果s[i]分解出来的质因数不能包含M的所有质因数的话,就永远不能整除M(即使乘方(分裂)后)。

当然并不需要真的把s[i]分解为质因数,只要有一个s[i]%pj(j=1..k)!=0就说明是-1。

3.确定不是-1后,需要计算最小分裂时间。

当s[i]^ans中包含的每个pi的个数(假设为bi)比M中包含的pi的个数(即ai*m2)多时,就能被M整除。

所以就是找到最小的ans,使每个bi>=(ai*m2)(i=1...n)。

这个ans=ceil(a[i]*m2/b[i]) (只适用于a[i]*m2比b[i]大时)(ceil表示向上取整)

在所有s[i]中寻找最小的ans就是答案。

#include<algorithm>
#include<cmath>
#include<cstring>
#include<cctype>
using namespace std;
const int maxm=30010,maxn=10010,inf=0x3f3f3f3f,eps=1e-6;
int pri[maxm],num[maxm],num2[maxm],s[maxn],tot=0,n,m,m2,ans;
int main()
{
//    freopen("cell.in","r",stdin);
//    freopen("cell.out","w",stdout);
    scanf("%d",&n); scanf("%d%d",&m,&m2);
    for(int i=2;i*i<=m;i++)
     if(m%i==0)
      {
        pri[++tot]=i;
        while(m%i==0)m/=i,num[tot]++;
      } 
    if(m!=1)pri[++tot]=m,num[tot]=1;
    for(int i=1;i<=tot;i++)num[i]*=m2;
    ans=inf;//for(int i=1;i<=tot;i++)printf("[%d]%d %d\n",i,pri[i],num[i]);
    for(int i=1;i<=n;i++)
     {
         scanf("%d",&s[i]);
         int f=0;
         memset(num2,0,sizeof(num2));
         for(int j=1;j<=tot;j++)
          if(s[i]%pri[j]!=0)f=1;
         if(f)continue;
         for(int j=1;j<=tot;j++)
          while(s[i]%pri[j]==0)
           num2[j]++,s[i]/=pri[j];
         int maxs=0;
         for(int j=1;j<=tot;j++)
          if(num[j]>num2[j])//printf("[%d]num=%d,num2=%d\n",j,num[j],num2[j]),
           maxs=max(maxs,(int)(ceil(1.0*num[j]/num2[j])+eps));
//         printf("%d\n",maxs);
         ans=min(ans,maxs);
     }
    if(ans==inf)ans=-1;
    printf("%d",ans);
    return 0;
}

二、三国游戏

题解:

每一行(或一列,实际上行和列是相同的)最大数必拿不到

因为稽器人针对你,你也可以针对稽器人

其次可以发现一个性质

每一行次大数必能拿到

你先拿走i,然后稽器人拿走第i行最大数,然后你就能拿到第i行次大了

最后可以发现一个性质

既然每一行的最大数没人拿得到,而次大数你又必能拿到

那直接挑一个最大的次大数就vans了

#include<iostream>
#include<cstdio>
using namespace std;
int n,a[510][510];
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)for(int j=1;j+i<=n;++j){
        scanf("%d",&a[i][j+i]);
        a[j+i][i]=a[i][j+i];
    }
    int ans=0;
    for(int i=1;i<=n;++i){
        int mx1=0,mx2=0;
        for(int j=1;j<=n;++j){
            if(a[i][j]>mx1)  mx2=mx1,mx1=a[i][j];
            else if(a[i][j]>mx2)  mx2=a[i][j];
        }
        ans=max(ans,mx2);
    }
    cout<<1<<endl<<ans<<endl;
    return 0;
}

三、守望者的逃离

1.搜索

记忆化搜索即可,但是原则上,如果内存限制不够大的话30w的dfs会爆栈,这里不应该用dfs而应该用bfs
先搜索出所有 f[i][j]的结果,f[i][j]表示第i秒,剩余魔法是j能走的最远距离f[i][j]表示第 i 秒,剩余魔法是 j 能走的最远距离,最后遍历整个 f 数组找一个最优解即可

#include<bits/stdc++.h>
using namespace std;
int m,s,t,ans,anstime;
int f[300105][15]={0};
void dfs(int nowtime,int way,int value){
    if (nowtime > t) return;        //若超出规定时间t则结束搜索
    if (f[nowtime][value] >= way) return;  //对当前状态判断是否比之前记录过的状态更优,若更优才继续搜索
    f[nowtime][value] = way;
    if (way >= s) return;     // 若当前移动的距离已经超过目标距离了,那么之后的搜索都是无意义的
    if (value >= 10){    //若当前蓝量足够闪烁,那么显然立刻闪烁是最优解
        dfs(nowtime+1,way+60,value-10);
    }
    else{
        //搜索移动和停止在原地回蓝两种状态
        dfs(nowtime+1,way,value+4);
        dfs(nowtime+1,way+17,value);
    }
}

int main() 
{
    memset(f,-1,sizeof(f));
    scanf("%d%d%d",&m,&s,&t);
     int starttime = 0,start = 0;
    while (m >= 10 && starttime < t && start < s){
        //毋庸置疑,一开始将初始有的蓝全部用完
        m-=10;
        start+=60;
        ++starttime;
    }
    //两个特殊情况,即蓝没用完,却已经逃离了,或蓝没用完,时间到了
    if (start >= s){
        printf("Yes\n%d",starttime);
        return 0;
    }
    if (starttime == t){
        printf("No\n%d",start);
        return 0;
    }
    dfs(starttime,start,m);
    int ans = 0;
    //在所有的状态中搜索一个最优解,若某次搜索发现已经逃离,那么当前时间就是最小时间
    for(int i = 1 ; i <= t ; ++i)
        for (int j = 0; j <= 14 ; ++j)
        {
            ans = max(ans,f[i][j]);
            if (f[i][j] >= s){
                printf("Yes\n%d",i);
                return 0;
            }
        }     
     printf("No\n%d",ans);
     return 0;
}

2.dp

用F[t]表示守望者在t时间内所能走的最远距离,G[t]表示守望者只使用闪烁法术在t时间内所能走的最远距离。 则相应的状态转移方程为
F[0]=G[0]=0

F[t]=max(F[t−1]+17,G[t])

#include <stdio.h>

int main() {
    int m, s, t;
    int i, d1, d2; 
    scanf("%d%d%d", &m, &s, &t);
    d1 = d2 = 0;
    for (i = 1; i <= t; i++) {
        if (m >= 10) {
            d1 += 60; m-= 10;
        } else {
            m += 4;
        }
        if (d1 > d2 + 17) d2 = d1;
        else d2 += 17;
        if (d2 >= s) break;
    }
    if (i > t) printf("No\n%d\n", d2);
    else printf("Yes\n%d\n", i);
    return 0;
}
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值