其他分享
首页 > 其他分享> > 《进阶指南》练习0x02

《进阶指南》练习0x02

作者:互联网

练习网址

A

最直接的想法其实是状压:

signed main()
{
    int n, tot;
    cin >> n;
    tot = 1 << n;
    for (int i = 0; i < tot; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            if (i >> j & 1)
                cout << j + 1 << ' ';
        }
        cout << '\n';
    }
    return 0;
}

DFS的话就这样写:

void dfs(int now, int state) 
{
    if (now == n)
    {
        for (int i = 0; i < n; i++)
            if (state >> i & 1)
                cout << i + 1 << " ";
        cout << '\n';
        return;
    }
    dfs(now + 1, state);            
    dfs(now + 1, state | (1 << now)); 
}

B

DFS这样写:

void dfs(int pos, int state)
{
    if (__builtin_popcount(state) == k)
    {
        for (int i = 0; i < n; ++i)
        {
            if (state >> i & 1)
                cout << i + 1 << ' ';
        }
        cout << '\n';
    }
    for (int i = pos; i <= n; ++i)
    {
        dfs(i + 1, state | (1 << (i - 1)));
    }
}
signed main()
{
    cin >> n >> k;
    dfs(1, 0);
    return 0;
}

如果不要求字典序,有一个复杂度与答案同阶的巧妙算法:

void GospersHack(int k, int n)
{
    int cur = (1 << k) - 1;
    int limit = (1 << n);
    while (cur < limit)
    {
        for (int i = 0; i < n; ++i)
        {
            if (cur >> i & 1)
                cout << i + 1 << ' ';
        }
        cout << '\n';
        int lb = cur & -cur;
        int r = cur + lb;
        cur = ((r ^ cur) >> __builtin_ctz(lb) + 2) | r;
        // cur = (((r ^ cur) >> 2) / lb) | r;
    }
}

感谢 Pecco 在他的 知乎专栏 中介绍这一算法。

C

经典DFS:

bool vis[maxn];
int n, ans[maxn];
void dfs(int cnt)
{
    if (cnt == n)
    {
        for (int i = 1; i <= n; ++i)
            cout << ans[i] << ' ';
        cout << '\n';
    }
    for (int i = 1; i <= n; ++i)
    {
        if (!vis[i])
        {
            vis[i] = 1;
            ans[cnt + 1] = i;
            dfs(cnt + 1);
            vis[i] = 0;
        }
    }
}
signed main()
{
    cin >> n;
    dfs(0);
    return 0;
}

也可以用 next_permutation

D

很经典的题,状压枚举第一行以后判断。

const int inf = 0x3f3f3f3f;
int a[10][10], t[10][10], ans;
int solve(int state)
{
    int cnt = __builtin_popcount(state);
    for (int i = 1; i <= 5; ++i)
        for (int j = 1; j <= 5; ++j)
            t[i][j] = a[i][j];
    for (int i = 0; i < 5; ++i)
    {
        if (state >> i & 1)
        {
            t[1][i + 1] = !t[1][i + 1];
            t[2][i + 1] = !t[2][i + 1];
            if (i > 0)
                t[1][i] = !t[1][i];
            if (i < 4)
                t[1][i + 2] = !t[1][i + 2];
        }
    }
    for (int i = 1; i < 5; ++i)
    {
        for (int j = 1; j <= 5; ++j)
        {
            if (!t[i][j])
            {
                t[i][j] = !t[i][j];
                t[i + 1][j] = !t[i + 1][j];
                if (j > 1)
                    t[i + 1][j - 1] = !t[i + 1][j - 1];
                if (j < 5)
                    t[i + 1][j + 1] = !t[i + 1][j + 1];
                if (i < 4)
                    t[i + 2][j] = !t[i + 2][j];
                cnt++;
            }
        }
    }
    for (int i = 1; i <= 5; ++i)
        for (int j = 1; j <= 5; ++j)
            if (!t[i][j])
                return inf;
    return cnt;
}

signed main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        for (int i = 1; i <= 5; ++i)
            for (int j = 1; j <= 5; ++j)
                scanf("%1d", &a[i][j]);
        ans = inf;
        for (int i = 0; i < 32; ++i)
            ans = min(ans, solve(i));
        printf("%d\n", ans <= 6 ? ans : -1);
    }
    return 0;
}

E

首先我们知道普通汉诺塔问题的答案是 \(dp[i]=2\times dp[i-1]+1=2^i-1\),其实是先把上面的 \(i-1\) 个移到 \(B\) 柱上,把最后一个移到 \(C\) 上,再把 \(B\) 上的 \(i-1\) 个移到 \(C\) 上。

如果多一个柱子,考虑把 \(i\) 个盘子先放到 \(B\) 上在放到 \(D\) 上,那么剩下的 \(n-i\) 个盘子就转化成了 \(3\) 个柱子的问题,也就是说 \(ans[n]=min(ans[n],2\times ans[i]+dp[n-i])\)。

int dp[maxn], ans[maxn];
signed main()
{
    for (int i = 1; i <= 15; ++i)
        dp[i] = (1 << i) - 1;
    memset(ans, 0x3f, sizeof(ans));
    ans[0] = 0, ans[1] = 1;
    for (int i = 2; i <= 15; ++i)
    {
        for (int j = 0; j < i; ++j)
            ans[i] = min(ans[i], ans[j] * 2 + dp[i - j]);
    }
    for (int i = 1; i <= 12; ++i)
        cout << ans[i] << endl;
    return 0;
}

F

首先肯定要分解因数,那么 \(a\) 可以表示为 \(\large \prod p_i^{k_i}\),\(a\) 的约数和为 \(\large\prod_{i=1}^n \sum_{j=0}^{k_i}p_i^j\)。

第一步是唯一分解定理,第二步是约数和公式,可以用乘法原理推。

现在 \(a\) 变成了 \(a^b\),就是 \(\large \prod p_i^{b\times k_i}\),\(\large \sum_{j=0}^{k_i}p_i^j\) 这部分是等比数列求和,相当于 \(\dfrac{p_i^{b\times k_i+1}-1}{p_i-1}\),分子快速幂处理,分母用费马小定理求出逆元即可。

const ll mod = 9901;
ll ksm(ll b, ll k)
{
    b %= mod;
    ll ret = 1;
    while (k)
    {
        if (k & 1)
            ret = ret * b % mod;
        b = b * b % mod;
        k >>= 1;
    }
    return ret;
}
signed main()
{
    ll a, b, ans = 1;
    cin >> a >> b;
    if (a == 0)
    {
        cout << 0 << endl;
        return 0;
    }
    for (ll i = 2; i * i <= a; ++i)
    {
        int cnt = 0;
        while (a % i == 0)
            cnt++, a /= i;
        if (cnt)
            ans = ans * (ksm(i, b * cnt + 1) - 1 + mod) % mod * ksm(i - 1, mod - 2) % mod;
    }
    if (a > 1)
        ans = ans * (ksm(a, b + 1) - 1 + mod) % mod * ksm(a - 1, mod - 2) % mod;
    cout << ans << endl;
    return 0;
}

G

一年多之前做过,懒得写了。POJ3889

分治。

H

很无聊的问题,用栈模拟DFS。

标签:指南,进阶,int,ll,cout,0x02,state,ans,mod
来源: https://www.cnblogs.com/theophania/p/lyd0x02.html