《进阶指南》练习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