');}

NOIP2010 提高组

机器翻译

队列。

  1. #include <cstdio>
  2. int m, n; //内存容量和文章的长度
  3. int nc[10001], head = 1, tail = 1; //内存中存的单词以及首尾,手写队列
  4. int t[1001]; //查看是否已经出现
  5. int main() {
  6. scanf("%d %d", &m, &n);
  7. int word, ans = 0;
  8. for (int i = 1; i <= n; i++) {
  9. scanf("%d", &word);
  10. if (t[word] == 0) { //还没有存
  11. ans++;
  12. if (tail - head < m) { //还没有填满
  13. nc[tail] = word; //将 word 存入内存中
  14. tail++;
  15. t[word] = 1; //标记这个单词
  16. } else { //已经填满
  17. t[nc[head]] = 0; //释放头元素
  18. head++;
  19. nc[tail] = word;
  20. tail++;
  21. t[word] = 1;
  22. }
  23. }
  24. }
  25. printf("%d\n", ans);
  26. return 0;
  27. }

乌龟棋

DP问题,每次枚举上一步 + 当前棋子个数就可以

  1. #include <cstdio>
  2. int step[351], card[5];
  3. int DP[41][41][41][41]; //使用 i 张 1, j 张 2, k 张 3, q 张 4 最大值
  4. int max(int a, int b) {
  5. if (a > b) return a;
  6. return b;
  7. }
  8. int main() {
  9. int n, m;
  10. scanf("%d %d", &n, &m);
  11. for (int i = 1; i <= n; i++)
  12. scanf("%d", &step[i]);
  13. for (int i = 1; i <= m; i++) {
  14. int tmp;
  15. scanf("%d", &tmp);
  16. card[tmp]++;
  17. }
  18. DP[0][0][0][0] = step[1];
  19. for (int i = 0; i <= card[1]; i++) {
  20. for (int j = 0; j <= card[2]; j++) {
  21. for (int k = 0; k <= card[3]; k++) {
  22. for (int q = 0; q <= card[4]; q++) {
  23. int l = 1 + i + j * 2 + k * 3 + q * 4;
  24. if (i != 0)
  25. DP[i][j][k][q] = max(DP[i][j][k][q], DP[i - 1][j][k][q] + step[l]);
  26. if (j != 0)
  27. DP[i][j][k][q] = max(DP[i][j][k][q], DP[i][j - 1][k][q] + step[l]);
  28. if (k != 0)
  29. DP[i][j][k][q] = max(DP[i][j][k][q], DP[i][j][k - 1][q] + step[l]);
  30. if (q != 0)
  31. DP[i][j][k][q] = max(DP[i][j][k][q], DP[i][j][k][q - 1] + step[l]);
  32. }
  33. }
  34. }
  35. }
  36. printf("%d\n", DP[card[1]][card[2]][card[3]][card[4]]);
  37. return 0;
  38. }

关押罪犯

并查集 + 贪心

  1. #include <cstdio>
  2. #include <algorithm>
  3. int n, m; //罪犯的数目以及存在仇恨的罪犯对数
  4. int father[100001], enemy[100001];
  5. struct data {
  6. int a, b, c;
  7. } f[100001];
  8. bool cmp(data x, data y) { //从大到小排序
  9. return x.c > y .c;
  10. }
  11. int find_father(int x) {
  12. if (x == father[x])
  13. return x;
  14. father[x] = find_father(father[x]);
  15. return father[x];
  16. }
  17. bool check(int a, int b) {
  18. if (find_father(a) == find_father(b)) {
  19. return true;
  20. }
  21. return false;
  22. }
  23. void add(int x, int y) {
  24. x = find_father(x);
  25. father[x] = find_father(y);
  26. }
  27. int main() {
  28. scanf("%d %d", &n, &m);
  29. for (int i = 1; i <= m; i++) {
  30. scanf("%d %d %d", &f[i].a, &f[i].b, &f[i].c);
  31. }
  32. for (int i = 1; i <= n; i++) {
  33. father[i] = i;
  34. }
  35. std::sort(f + 1, f + 1 + m, cmp);
  36. for (int i = 1; i <= m; i++) {
  37. if (check(f[i].a, f[i].b) == true) {
  38. printf("%d\n", f[i].c);
  39. return 0;
  40. }
  41. if (enemy[f[i].a] == 0) enemy[f[i].a] = f[i].b;
  42. else add(enemy[f[i].a], f[i].b); //f[i].a 已经有敌人了,因此把 f[i].b 同 f[i].a 的敌人关在一起更好
  43. if (enemy[f[i].b] == 0) enemy[f[i].b] = f[i].a;
  44. else add(enemy[f[i].b], f[i].a); //把他们两个关进同一所监狱中
  45. }
  46. printf("%d\n", 0);
  47. return 0;
  48. }

引水入城

①当无解时,很好处理,把挨着湖泊的那一行城市每个都 DFS 一遍,看看最后一排点是否都被经过。

②当有解时,首先可以想到搜索挨着湖泊的那一行城市,然后看每个城市的影响区间是多少。

影响区间是连续的,证明可以见 [洛谷-天上一颗蛋的博客]

因此,可以 DFS 出每个蓄水厂的影响区间 leftright。然后就可以得到以下这张图:

其中,矩形代表挨着沙漠的城市,每根线段表示每个蓄水厂的影响范围。

接着,进行 DP。

DP[i] 代表第 i 个城市是否选择作为蓄水厂。因此就可以得到 DP[i] = min(DP[j] + 1) 的方程式,其中 j 是所有右节点 + 1大于等于 i 的左节点(也就是 i 和 j 能够拼成一条更长的线段)的线段。

DP的具体实现:

首先将所有的 DP 赋值为 MAX,然后,起点必须要从左端点为 1 的线段进行转移,因此把所有左端点为 1 的点的 DP 值都设为 1 ,然后进行 DP。当每根线段的右端点恰好为 m 时,更新答案。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. int high[501][501], vis[501][501], DP[501];
  5. struct node {
  6. int left, right;
  7. } a[501][501]; //每个点能够到达最左和最右的位置
  8. bool cmp(node x, node y) {return x.left < y.left;}
  9. int n, m;
  10. int xx[4] = {0, 1, -1, 0};
  11. int yy[4] = {1, 0, 0, -1};
  12. int max(int a, int b) {return a > b ? a : b;}
  13. int min(int a, int b) {return a < b ? a : b;}
  14. void DFS(int x, int y) {
  15. vis[x][y] = 1;
  16. if (x <= 0 || x > n || y <= 0 || y > m) return;
  17. for (int i = 0; i < 4; i++) {
  18. int nx = x + xx[i], ny = y + yy[i];
  19. if (high[nx][ny] < high[x][y] && nx >= 1 && nx <= n && ny >= 1 && ny <= m) {
  20. if (vis[nx][ny] == false)
  21. DFS(nx, ny);
  22. a[x][y].left = min(a[nx][ny].left, a[x][y].left);
  23. a[x][y].right = max(a[nx][ny].right, a[x][y].right);
  24. }
  25. }
  26. }
  27. int main() {
  28. //将 left 设置为最大值
  29. for (int i = 1; i <= 500; i++)
  30. for (int j = 1; j <= 500; j++)
  31. a[i][j].left = 214748364;
  32. scanf("%d %d", &n, &m);
  33. for (int i = 1; i <= n; i++)
  34. for (int j = 1; j <= m; j++)
  35. scanf("%d", &high[i][j]);
  36. for (int i = 1; i <= m; i++)
  37. a[n][i].left = a[n][i].right = i;
  38. for (int i = 1; i <= m; i++)
  39. if (vis[1][i] == false)
  40. DFS(1, i);
  41. int cnt = 0;
  42. for (int i = 1; i <= m; i++)
  43. if (vis[n][i] == false)
  44. cnt++;
  45. if (cnt) {
  46. printf("0\n%d\n", cnt);
  47. return 0;
  48. }
  49. std::sort(a[1] + 1, a[1] + 1 + m, cmp);
  50. memset(DP, 0x3f, sizeof(DP));
  51. for (int i = 1; i <= m; i++) {
  52. if (a[1][i].left == 1) {
  53. DP[i] = 1;
  54. }
  55. }
  56. int ans = 2147483647;
  57. for (int i = 1; i <= m; i++) {
  58. for (int j = 1; j <= m; j++) {
  59. if (i != j) {
  60. if (a[1][j].right + 1 >= a[1][i].left)
  61. DP[i] = min(DP[i], DP[j] + 1);
  62. if (a[1][i].right == m)
  63. ans = min(ans, DP[i]);
  64. }
  65. }
  66. }
  67. printf("1\n%d\n", ans);
  68. return 0;
  69. }