校 第三次热身赛

A.Buy Lottery Tickets

根据题意直接进行暴力dfs,因为一个return 错了四次 ;

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], ans[N];
bool st[N];
int m;

void dfs(int u)
{
    if(u > 6)
    {
        for(int i = 1; i <= 6 ; i ++)
        {
            cout << ans[i] << " ";
        }
        cout << endl;
        return ; //此处忘记写return直接WA四发
    }
    
    for(int i = 1 ; i <= m ; i ++)
    {
        if(!st[i] && ans[u - 1] < a[i])
        {
            ans[u] = a[i];
            u ++;
            st[i] = true;
            dfs(u);
            u --;
            st[i] = false;
        }
    }
}

int main()
{
    while(cin >> m)
    {
        memset(a , 0 , sizeof a);
        memset(ans , 0 , sizeof ans);
        memset(st , false , sizeof false);
        
        if(m == 99)
        {
            cout << endl;
            break;
        }
        if(m <= 6 || m >= 13)
        {
            cout << "ERROR" << endl;
            cout << endl;
            continue;
        }
        for(int i = 1; i <= m ; i ++) cin >> a[i];
        
        dfs(1);
        cout << endl;
    }
}

B.Professor's Task

没啥含金量 直接代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];

int main()
{
    string s;
    while(getline(cin,s))
    {
        memset(a , 0 , sizeof a);
        for(int i = 0 ;i < s.size() ; i++)
        {
            int t = s[i] - 0;
            if(t >= 97 && t <= 122)
                a[s[i]-97] ++;
        }
        for(int i = 0; i < 26 ; i++)
        {
            cout<<char(i+97) <<':'<< a[i]<<endl;
        }
        cout << endl;
    }
 
    return 0;
}

C.Golden Coins

完全背包(类似)

对于题目所给的意思,也就是说拥有1-17这些平方的纸币,给出一个数(小于300)问用前面这些价值的纸币有多少种方法来凑出这个数;

转化一下问题,就是说:

有17种物品 ,每个物品的体积都是本身的平方 ,给出一个背包体积,问有多少种方法恰好将这个背包装满;

dp问题的难点就在于找状态转移方程的计算;

f[i][j] : 表示在前i个物品中选,且总体积为j的方案数;
状态转移方程计算:
1.如果不包含第 i 个物品 也就是题目所说的纸币; f[i][j] = f [i-1][j];
2.包含第i个物品 并且选k个这个物品 f[i][j] = f[i - 1][j - k * v[i]]; 转换的思想 选第i个也就是说 在前i-1选体积为j-v[i]的方案数;
    
写状态转移方程的时候一定需要保证不重不漏;
此处题解写不优化的完全背包:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int v[N] , f[N][N];

int main()
{
    
    for(int i = 0; i <= 17 ; i ++) //预处理体积
        v[i] = i * i; 
    
    int m;
    
    while(cin >> m)
    {
        if(m == 0) break;
        
        f[0][0] = 1; //初始化;
        
        for(int i = 1;i <= 17;i ++)
        {
            for(int j = 0;j <= m;j ++)
            {
                for(int k = 0;v[i] * k <= j;k ++)
                {
                    if(k == 0) f[i][j] = f[i - 1][j]; //k等于0的时候就是不选第i这种情况
                    else f[i][j] += f[i - 1][j - k * v[i]];
                }
            }
        }
        cout << f[17][m] << endl;
    }
    return 0;
}
下面是优化的代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int v[N] , f[N][N];

int main()
{
    
    for(int i = 0; i <= 17 ; i ++) //预处理体积
        v[i] = i * i; 
    
    int m;
    
    while(cin >> m)
    {
        if(m == 0) break;
        
        f[0][0] = 1; //初始化;
        
        for(int i = 1;i <= 17;i ++)
        {
            for(int j = 0;j <= m;j ++)
            {
                for(int k = 0;v[i] * k <= j;k ++)
                {
                    if(k == 0) f[i][j] = f[i - 1][j]; //k等于0的时候就是不选第i这种情况
                    else f[i][j] += f[i - 1][j - k * v[i]];
                }
            }
        }
        cout << f[17][m] << endl;
    }
    return 0;
}

D.Happy 2004

数学知识,数论;
A 被分解质因数为:
一个数A = p1^k1 + p2^k2 + p3^k3 .....pn^kn ;一个数A可以为拆分为p1的k1次方 + p2的k2次方 +一直相加;
所以易得 A^B = p1^(k1*B) + p2^(k2*B) +....+pn^(kn*B);

约数个数: (k1+1)*(k2+1)*....*(kn+1);

A的约数之和:  (p1^0+...p1^k1)     *     (p2^0+....+p2^k2)     *...*     (pn^0 +...+ pn^kn);
A^B的约数之和:(p1^(0*B) +...+ p1^(k1*B)) * (p2^(0*B) +....+ p2^(k2*B)) *...* (pn^(0*B) +...+ pn^(kn*B));

所以另sun(p,k) = (p^0+....+p^(k-1));这也是本题要用的重点公式,所以我们需要分析如何求这个sum(p,k);

相当于结论:
//这是k为偶数的条件下;
sum(p,k) = p^0+....+p^(k-1)
         = p^0+...+p^(k/2 - 1)) + (p^(k/2) +...+p^(k - 1)); //发现后一项中都含有p^(k/2);
         = p^0+...+p^(k/2 - 1)) + p^(k/2) * (p^0 + ...+ p^(k/2 - 1));
         = (1 + p^(k/2)) * (p^0 + ...+ p^(k/2 - 1));
         = (1 + p^(k/2)) * sum(p , k/2);
//k为奇数时;
sum(p,k) = p^0+....+p^(k-1)
         = p^0 + p^1 +...+ p^(k-1) //拿出第二项;
         = p^0 + p^1 * (p^0 + p^1 + ... + p^(k - 2))
         = p^0 + p^1 * sum(p,k - 1); 
         = 1 + p * sum(p , k - 1);
         
sum(p,k) = p^0+....+p^(k-1)
         = p^0 + p^1 +...+ p^(k-2) +p^(k-1);
         = sum(p , k - 1) + p^(k-1);  

#include <bits/stdc++.h>
using namespace std;
const int mod = 29;
int m;

int quick_pow(int a , int b) //模板快速幂 a的b次方;
{
    int res = 1;
    
    while(b)
    {
        if(b & 1) res = res * a % mod ;
        a = a * a % mod;
        
        b >>= 1;
    }
    return res;
}

int sum(int p , int k)
{
    if(k == 1) return 1;//只有一项那么 p^0就是1;
    //如果是偶数
    if(k % 2 == 0)
        return (1 + quick_pow(p , k/2)) * sum(p , k/2) % mod; 
        
    //如果是奇数
    if(k % 2)
        return (sum(p , k - 1) + quick_pow(p , k-1)) % mod; //对应上面奇数的第二个公式;
}

int main()
{
    int b;
    while(cin >> b && b != 0)
    {
        int a = 2004;
        int ans = 1;
        for(int i = 2 ; i <= a / i ; i ++) //对a进行因式分解;
        {
            if(a % i == 0)
            {
                int s = 0;
                while(a % i == 0)
                {
                    a /= i;
                    s ++; //这个s就是i的几次方;
                }
                ans = ans * sum(i , s * b + 1) % mod; //也就是求约数之和的公式; 注意要+1,不理解看上面公式
            }
        }
        
        if(a > 1) ans = ans * sum(a , b + 1) % mod; //如果最后a>1说明存在一个质数;那么我们就再算一次;
        if(a == 0) ans = 0;
        cout << ans << endl;
    }
    
}
posted @ 2023-12-23 22:20  不过是过客  阅读(4)  评论(0编辑  收藏  举报