TEXT Data Structures
不能为了用数据结构而用数据结构……算法有这样的需求,如:找最大、动态维护集合、判重……才去想可用的东西,如:堆、BST、哈希表。还有STL。
数据结构的选择首先看是否能工作(和问题是否配套、空间有没有爆),其次看编码和调试的难易,最后才是时间复杂度。
这篇文章介绍了二叉搜索树、哈希表、Trie、堆。作者建议我们写朴素的二叉搜索树,而非Treap、Splay等等,如果担心数据很糟糕就打乱顺序插入。是个好办法,不过个人认为BST写起来也不太麻烦。
TEXT Dynamic Programming
动态规划。
PROB Preface Numbering
告诉你罗马数字的书写规则,求1~n中分别出现了多少个I、V、X、L、C、D、M,1 <= n < 3500。
有点麻烦的是4、9要用减法表示的特殊规则,我特判了一下。
/*
ID: chrt2001
PROG: concom
LANG: C++
*/
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 100, MAX_P = 50;
bool control[MAX_N+1][MAX_N+1];
int n, M[MAX_N+1][MAX_N+1], p[MAX_N+1];
queue<int> Q;
void search(int x)
{
memset(p+1, 0, sizeof(int)*n);
Q.push(x);
control[x][x] = true;
int u;
while (!Q.empty()) {
u = Q.front();
Q.pop();
for (int v = 1; v <= n; ++v)
if (M[u][v]) {
p[v] += M[u][v];
if (p[v] > MAX_P && !control[x][v]) {
Q.push(v);
control[x][v] = true;
}
}
}
}
int main()
{
freopen("concom.in", "r", stdin);
freopen("concom.out", "w", stdout);
int m;
scanf("%d", &m);
for (int i = 0; i < m; ++i) {
int x, y, p;
scanf("%d %d %d", &x, &y, &p);
n = max(n, max(x, y));
M[x][y] += p;
}
for (int i = 1; i <= n; ++i)
search(i);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (control[i][j] && i != j)
printf("%d %d\n", i, j);
return 0;
}
PROB Subset Sums
将{1, 2, …, n}划分为两个集合,使得和相等。
等价于取{1, 2, …, n}的一个子集,使得和为n(n+1)/4。规定空集的和为0。令f[i][j] = {1, 2, …, i}中和为j的子集数,建立递推。
/*
ID: chrt2001
PROG: subset
LANG: C++
*/
#include <cstdio>
using namespace std;
long long f[40][391];
int main()
{
freopen("subset.in", "r", stdin);
freopen("subset.out", "w", stdout);
int n;
scanf("%d", &n);
int sum = n*(n+1)/2;
if (sum & 1) {
puts("0");
return 0;
}
sum /= 2;
f[0][0] = f[1][0] = f[1][1] = 1;
for (int i = 2; i <= n; ++i)
for (int j = 0; j <= sum; ++j)
f[i][j] = f[i-1][j] + (j >= i ? f[i-1][j-i] : 0);
printf("%lld\n", f[n][sum]/2);
return 0;
}
PROB Runaround Numbers
寻找第一个大于M且具有如下性质的数:
1. 各个位互不相同且不含0。
2. 从最高位开始,这一位是几就往右挪几步,挪到最低位“右边”是最高位,会停在某一位,从这一位开始,重复这一步骤。要求最后能回到最高位,且每一位都曾作为起点。
题目只说数据保证答案在unsigned long long
范围,其实,注意到第一个性质,答案最多是9位数。我们甚至可以枚举了:9! = 362880。现阶段枚举不超过11个元素的全排列是可接受的,只要check不是很繁琐:11! = 39916800。
一开始读错了题,不知道要求每一位都要作一次起点,于是又套一个递归在间隙里填数……
/*
ID: chrt2001
PROG: runround
LANG: C++
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int p[9] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8}, INF = 1<<30;
int m, ans = INF, a[9];
bool used[10];
// 总共的位数,已经填了的位数,现在考虑的数位,现在的数值
void solve(int d, int n, int k, int x)
{
if (x >= ans)
return;
if (a[k]) {
if (k == 0 && n == d && x > m)
ans = min(ans, x);
return;
}
for (int i = 1; i <= 9; ++i)
if (!used[i]) {
a[k] = i;
used[i] = true;
solve(d, n+1, (k+i)%d, x+i*p[d-1-k]);
used[i] = false;
}
a[k] = 0;
}
int main()
{
freopen("runround.in", "r", stdin);
freopen("runround.out", "w", stdout);
int d = 0;
scanf("%d", &m);
for (int i = m; i; i /= 10)
++d;
for (int i = max(2, d); i <= 9; ++i)
for (int j = 1; j <= 9; ++j) {
if (j%i == 0)
continue;
a[0] = j;
used[j] = true;
solve(i, 1, j%i, j*p[i-1]);
if (ans != INF) {
printf("%d\n", ans);
return 0;
}
used[j] = false;
}
return 0;
}
PROB Party Lamps
之前作为穷举的例题在TEXT里讲过。
我枚举了最终状态,验证是否可行。ANALYSIS是减少按开关的次数后搜的,做的比我更好的地方是,还注意到灯的状态每隔6一循环。值得学习。
/*
ID: chrt2001
PROG: lamps
LANG: C++
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 100;
bool on[MAX_N], off[MAX_N];
char st[16][MAX_N+1];
int n, od[16], top;
inline bool cmp(int i, int j)
{
return strcmp(st[i], st[j]) < 0;
}
void add(int x)
{
int a;
for (int i = 0; i < n; ++i) {
a = 1;
if (x & 0x8)
a ^= 1;
if ((x & 0x4) && (i & 0x1))
a ^= 1;
if ((x & 0x2) && !(i & 0x1))
a ^= 1;
if ((x & 0x1) && i % 3 == 0)
a ^= 1;
if (on[i] && !a || off[i] && a)
return;
st[top][i] = '0' + a;
}
od[top] = top++;
}
int main()
{
freopen("lamps.in", "r", stdin);
freopen("lamps.out", "w", stdout);
int c, x;
scanf("%d %d", &n, &c);
while ((~scanf("%d", &x)) && (~x))
on[x-1] = true;
while ((~scanf("%d", &x)) && (~x))
off[x-1] = true;
int cnt;
for (int i = 0; i < 16; ++i) {
cnt = 0;
for (int j = i; j; j >>= 1)
if (j & 0x1)
++cnt;
if (c >= cnt && !(c-cnt & 0x1))
add(i);
}
if (top == 0)
puts("IMPOSSIBLE");
else {
sort(od, od+top, cmp);
for (int i = 0; i < top; ++i)
printf("%s\n", st[od[i]]);
}
return 0;
}