CSU 2069 Saruman’s Level Up(组合数+枚举)

CSU 2069 Saruman’s Level Up

题意

​ 求0-n之间,二进制中1的个数为3的倍数的数有多少个。

解题思路

  首先我们知道,剩余位数=length-1-当前下标。
  先将n化成二进制,先枚举1的数量(3的倍数),然后从高位往低位遍历,剩下的就是求组合数的和了。遇到1就先判断当前剩余1的个数和剩余位数的大小,就会有以下三种情况。
  1.如果剩余位数大于剩余1的个数(能放下剩余1),那么结果就加上后面的组合数,即C(剩余位数,剩余1的个数),然后剩余1的个数减一,继续后面的遍历。
  2.如果剩余1的个数和剩余位数相等时,这种情况就要满足剩余位数上全是1,如果全是1,结果加1,然后break,如果不全是1,则直接break。
  3.如果剩余位数小于当前剩余1的个数,那么就表示不能再放1了,这种情况直接break。
  最后得到的结果就是我们想求的答案。
  例如10的二进制是1010,遇到第一个1就加上C(3,3),到了后面那个1时,剩下2个1,然后剩下两位,所以我们要判断一下后面是否全为1,只有全为1的时候,结果才能加一,否则直接结束当前1的个数这种情况的循环。
  比赛的时候思路是对的,用c++求组合数,用了个简单的递归TLE到哭,死活想不通为什么会T,没想到回来用Java求组合数就过了,java大数还是强。。。

代码

import java.math.BigInteger;
import java.util.Scanner;
import java.util.Stack;

public class Main {
    static Long number[] = new Long[100];

    static long calc(int m, int n) {
        BigInteger t = BigInteger.ONE;
        for (int i = 1; i <= m; i++) {
            t=t.multiply(BigInteger.valueOf(i));
        }
        for (int i = 1; i <= n; i++) {
            t=t.divide(BigInteger.valueOf(i));
        }
        for (int i = 1; i <= m - n; i++) {
            t=t.divide(BigInteger.valueOf(i));
        }
        String str=t.toString();
        return Long.valueOf(str);
    }

    static int itoa(Long n) {
        Stack<Long> s = new Stack<Long>();
        int cnt = 0;
        while (n != 0) {
            s.push(n%2);
            n /= 2;
        }
        while (!s.empty()) {
            Long t = s.peek();
            s.pop();
            number[cnt++] = t;
        }
        return cnt;
    }

    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        long n;
        while (in.hasNext()){
            n=in.nextLong();
            System.out.printf("Day %d: Level = ",n);
            if(n<7){
                System.out.println("0");
                continue;
            }
            int len=itoa(n);
            Long sum=Long.valueOf(0);
            int flag=0;
            for(int tmp=3;tmp<=len;tmp+=3){
                int t=tmp;
                for(int i=0;i<len;i++){
                    if(t==0){
                        sum++;
                        break;
                    }
                    if(number[i]==1){
                        if(len-i>t){//当前节点为0时,加上组合数
                            sum+=calc(len-i-1,Math.min(len-i-1,len-i-1-t));
                            t--; //当前节点为1,1的个数减一
                        }
                        else if(len-i==t){
                            int fff=0;
                            for(int j=i;j<len;j++)//这里后面必须后面全为1,不然如果排列组合的话就会改变上界
                                if(number[j]==0) fff=1;
                            if(fff==0) sum++;
                            break;
                        }
                        else break;  //1的个数小于剩下位数
                    }
                }
            }
            System.out.println(sum);
        }
    }
}

补一份C++代码。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<stack>
using namespace std;

typedef long long LL;
int num[]= {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
char number[100];
int hass[70];
LL calc(int m,int n)
{
    memset(hass,0,sizeof(hass));
    for(int i=m; i>=1; i--)
    {
        int tmp=i,cnt=0;
        while(tmp!=1)
        {
            if(tmp%num[cnt]==0)
            {
                hass[num[cnt]]++;
                tmp/=num[cnt];
            }
            else cnt++;
        }
    }
    for(int i=m-n; i>=1; i--)
    {
        int tmp=i,cnt=0;
        while(tmp!=1)
        {
            if(tmp%num[cnt]==0)
            {
                hass[num[cnt]]--;
                tmp/=num[cnt];
            }
            else cnt++;
        }
    }
    for(int i=n; i>=1; i--)
    {
        int tmp=i,cnt=0;
        while(tmp!=1)
        {
            if(tmp%num[cnt]==0)
            {
                hass[num[cnt]]--;
                tmp/=num[cnt];
            }
            else cnt++;
        }
    }
    LL ans=1;
    for(int i=0;i<70;)
        if(hass[i]) {
            ans*=i;
            hass[i]--;
        }
        else i++;
    return ans;
}
void itoa(long long n)
{
    stack<char> s;
    int cnt=0;
    while(n)
    {
        s.push(n%2+'0');
        n/=2;
    }
    while(!s.empty())
    {
        char t=s.top();
        s.pop();
        number[cnt++]=t;
    }
    number[cnt]='\0';
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    long long n;
    while(cin>>n)
    {
        printf("Day %lld: Level = ",n);
        if(n<7)
        {
            printf("0\n");
            continue;
        }
        itoa(n);
        long long sum=0;
        int flag=0,len=strlen(number);
        for(int tmp=3; tmp<=len; tmp+=3)
        {
            int t=tmp;
            for(int i=0; i<len; i++)
            {
                if(t==0)
                {
                    sum++;
                    break;
                }
                if(number[i]=='1')
                {
                    if(len-i>t)
                    {
                        int a,b;
                        a=len-i-1,b=t;
                        if(a!=b)
                            b=min(a,a-b);
                        sum+=calc(a,b);
                        t--;
                    }
                    else if(len-i==t)
                    {
                        int fff=0;
                        for(int j=i; j<len; j++)
                            if(number[j]=='0') fff=1;
                        if(!fff)
                            sum++;
                        break;
                    }
                }
            }
        }
        printf("%lld\n",sum);
    }
    return 0;
}

对比一下时间,中南的OJ没有itoa()函数(小声逼逼)
时间

阅读更多
版权声明:随意转载,转载请注明出处。 https://blog.csdn.net/qq_36258516/article/details/79966049
个人分类: 暴力
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭