hdu 4610 Cards

题意:

给n张卡片,每张卡片上面有一个数,卡片的值取决于上面数字符合的条件:

1、这个数字是质数

2、其因子的数量是质数

3、其所有因子的和是质数

4、所有因子的乘积是一个平方数

符合几条,卡片的值就是多少。

给n种卡片,每种卡片上面数字是a,一共有b张。从中取k张,问如何取值最大。

还有一个规则是,要是取的所有k张卡片都不符合其中的某一条,则可加上该条规则对应的一个分数。


需要的知识点:

1、质数打表

2、求一个数的因子

3、二分快速幂


解题思路:

1、首先要把每张卡片对应的值求出来

      求出该数字的质因子及其个数,一个个判断条件。

      需要注意的是,题目问的是所有因子的数量、和、乘积是否是质数。

      我们求的是其质数因子的数量,第一个条件比较简单。

      要满足第二个条件,该数字只可能有一个质因子。判断其个数+1是不是质数就可以了。

      第三个条件,也必须只含一个素因子p^k.然后求1+p^1+p^2+..+p^k .判断是不是素数。

      具体分析、证明,点这个博客里面有。。

2、是否存在都不符合某些条件的情况以加分

      这里就直接枚举1<<4种情况(每一位代表该位代表的条件是否符合)

      先把所有卡片按已有的得分排序,从得分大的到得分小的符合条件的取


#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#define inf 0x3f3f3f3f
#define ll __int64
using namespace std;

struct node
{
    int a,b,score,s;
}num[1010];

bool prime[1000010];
int p[1000010],factor[100][2],prcnt;

int init()
{
    int i,j,cnt;
    memset(prime,true,sizeof prime);
    prime[0]=prime[1]=0;
    for(i=2;i<1000010;i++)
    {
        if(!prime[i]) continue;
        for(j=2;i*j<1000010;j++)
            prime[i*j]=0;
    }
    for(i=2,prcnt=0;i<1000010;i++)
    {
        if(prime[i])//把所有质数按顺序存下来
            p[prcnt++]=i;
    }
}

int getf(int x)
{
    int i,tmp=x,facnt=0;
    for(i=0;p[i]<=tmp/p[i];i++)//枚举可能的质因子(当然是质数)
    {
        factor[facnt][1]=0;//初始化 //[0]存质因子本身 [1]存该质因子个数
        if(tmp%p[i]==0)
        {
            factor[facnt][0]=p[i];
            while(tmp%p[i]==0)
            {
                factor[facnt][1]++;
                tmp/=p[i];
            }
            facnt++;
        }
    }
    if(tmp!=1)//一个质因子都没有找到 就只有自己咯
    {
        factor[facnt][0]=tmp;
        factor[facnt++][1]=1;
    }
    return facnt;
}

ll Pow(ll a,ll b)//二分的思想求幂
{
    ll ans = 1;
    while(b){
        if(b&1) ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}

int sum(int x,int y)
{
    if(x==0) return 0;
    if(y==0) return 1;
    if(y&1) return (1+Pow(x,y/2+1))*sum(x,y/2);
    else return (1+Pow(x,y/2+1))*sum(x,y/2-1)+Pow(x,y/2);
}

void check(int id)
{
    int facnt=getf(num[id].a);
    num[id].s=0;//s的二进制共四位表示四种条件 1或0表示是否满足该条件
    //第一个条件 判断是否是质数
    if(facnt==1&&factor[0][1]==1)
        num[id].s |=(1<<0);
    //第二个 因子(不等同于质数因子)个数是质数
    if(facnt==1&&prime[factor[0][1]+1])
        num[id].s|=(1<<1);
    //第三个 因子和是质数
    if(facnt==1&& prime[sum(factor[0][0],factor[0][1])])
        num[id].s|=(1<<2);
    //第四个 因子乘积为平方数
    int i,j,flag=1;
    for(i=0;i<facnt;i++)
    {
        ll tmp=(factor[i][1]+1)*factor[i][1]/2;
        for(j=0;j<facnt;j++)
        {
            if(i!=j)
               tmp=tmp*(factor[j][1]+1);
        }
        if(tmp%2!=0)
        {
            flag=0;
            break;
        }
    }
    if(flag) num[id].s|=(1<<3);
    num[id].score=0;
    for(i=0;i<4;i++)
    {
        if(num[id].s&(1<<i))
            num[id].score++;
    }
}

bool cmp(node a,node b)//按分数大小排序
{
    return a.score>b.score;
}

int main()
{
    int t,n,k,i,ii,j,ans,res,tmp,w[5];
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        for(i=0;i<n;i++)
        {
            scanf("%d%d",&num[i].a,&num[i].b);
            check(i);
            if(i!=0) putchar(' ');
            printf("%d",num[i].score);
        }
        puts("");

        for(i=0;i<4;i++)
            scanf("%d",&w[i]);
        sort(num,num+n,cmp);//按得分大小排序
        ans=-100000000;
        for(i=0;i<(1<<4);i++)
        {
            res=k;//记现在还要取多少张牌
            tmp=0;//现在这种情况的总得分
            ii=0;//取了的卡片占了哪些条件//不知道为什么一定要有这个量 直接枚举i不行吗
            for(j=0;j<n;j++)
            {
                if(!(num[j].s&i))//符合i条件的就取
                {
                    if(res==0) break;
                    ii|=num[j].s;
                    tmp+=(num[j].score*min(res,num[j].b));
                    res-=min(res,num[j].b);
                    if(res==0) break;
                }
            }
            if(res!=0) continue;
            for(j=0;j<4;j++)
            {
                if((ii&(1<<j))==0)
                    tmp+=w[j];
            }
            ans=max(ans,tmp);
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值