noip2010 真题练习 2017.2.18


  第一题比较简单,用exist数组判断是否在循环队列中,就可实现线性算法。

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<fstream>
 7 #include<sstream>
 8 #include<algorithm>
 9 #include<map>
10 #include<set>
11 #include<queue>
12 #include<vector>
13 #include<stack>
14 using namespace std;
15 typedef bool boolean;
16 #define INF 0xfffffff
17 #define smin(a, b) a = min(a, b)
18 #define smax(a, b) a = max(a, b)
19 template<typename T>
20 inline void readInteger(T& u){
21     char x;
22     int aFlag = 1;
23     while(!isdigit((x = getchar())) && x != '-');
24     if(x == '-'){
25         x = getchar();
26         aFlag = -1;
27     }
28     for(u = x - '0'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - '0');
29     ungetc(x, stdin);
30     u *= aFlag;
31 }
32 
33 int n, m;
34 int top;
35 int *s;
36 boolean exist[1002];
37 
38 inline void s_push(int* sta, int x) {
39     exist[sta[top] + 1] = false;
40     sta[top] = x;
41     exist[x + 1] = true;
42     if(top == m - 1)    top = 0;
43     else top = top + 1;
44 }
45 
46 int res = 0;
47 inline void solve() {
48     readInteger(m);
49     readInteger(n);
50     s = new int[(const int)(m + 1)];
51     memset(s, -1, sizeof(int) * (m + 1));
52     top = 0;
53     for(int i = 1, a; i <= n; i++) {
54         readInteger(a);
55         if(!exist[a + 1]) {
56             res++;
57             s_push(s, a);
58         }
59     }
60     printf("%d", res);
61 }
62 
63 int main() {
64     freopen("translate.in", "r", stdin);
65     freopen("translate.out", "w", stdout);
66     solve();
67     return 0;
68 }


  因为当卡牌的选择数是固定的情况下,跳的长度也就知道了,于是就可以用四种卡牌来做状态,得到了dp方程:

  为了防止过多的无用的状态,所以就用记忆化搜索(其实直接4个for也没什么问题)

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<fstream>
 7 #include<sstream>
 8 #include<algorithm>
 9 #include<map>
10 #include<set>
11 #include<queue>
12 #include<vector>
13 #include<stack>
14 using namespace std;
15 typedef bool boolean;
16 #define INF 0xfffffff
17 #define smin(a, b) a = min(a, b)
18 #define smax(a, b) a = max(a, b)
19 template<typename T>
20 inline void readInteger(T& u){
21     char x;
22     int aFlag = 1;
23     while(!isdigit((x = getchar())) && x != '-');
24     if(x == '-'){
25         x = getchar();
26         aFlag = -1;
27     }
28     for(u = x - '0'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - '0');
29     ungetc(x, stdin);
30     u *= aFlag;
31 }
32 
33 int n, m;
34 int counter[4];
35 int f[41][41][41][41];
36 boolean vis[41][41][41][41];
37 int *val;
38 
39 inline void init() {
40     readInteger(n);
41     readInteger(m);
42     memset(counter, 0, sizeof(counter));
43     memset(f, 0, sizeof(f));
44     memset(vis, false, sizeof(vis));
45     val = new int[(const int)(n + 1)];
46     for(int i = 1; i <= n; i++)        readInteger(val[i]);
47     for(int i = 1, a; i <= m; i++) {
48         readInteger(a);
49         counter[a - 1]++;
50     }
51     f[0][0][0][0] = val[1];
52     vis[0][0][0][0] = true;
53 }
54 
55 int dfs(int a, int b, int c, int d) {
56     if(vis[a][b][c][d])    return f[a][b][c][d];
57     vis[a][b][c][d] = true;
58     int pos = a * 1 + b * 2 + c * 3 + d * 4 + 1;
59     int ra = 0, rb = 0, rc = 0, rd = 0;
60     if(a > 0)    ra = dfs(a - 1, b, c, d);
61     if(b > 0)    rb = dfs(a, b - 1, c, d);
62     if(c > 0)    rc = dfs(a, b, c - 1, d);
63     if(d > 0)    rd = dfs(a, b, c, d - 1);
64     smax(f[a][b][c][d], max(ra, max(rb, max(rc, rd))) + val[pos]);
65     return f[a][b][c][d];
66 }
67 
68 inline void solve() {
69     int res = dfs(counter[0], counter[1], counter[2], counter[3]);
70     printf("%d", res);
71 }
72 
73 int main() {
74     freopen("tortoise.in", "r", stdin);
75     freopen("tortoise.out", "w", stdout);
76     init();
77     solve();
78     return 0;
79 }


  这道题相当于是安排罪犯,使所有的影响力的最大值最小,于是就不难想到二分答案。

  对于当前二分值mid,无视所有权值小于等于它的边,判断是否能让剩下的边构成二分图。至于这一步多次对未访问的点进行dfs,将与它相连的点丢进另一个监狱,如果出现矛盾,就说明当前的二分值不合法。

Code

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cctype>
  4 #include<cstring>
  5 #include<cstdlib>
  6 #include<fstream>
  7 #include<sstream>
  8 #include<algorithm>
  9 #include<map>
 10 #include<set>
 11 #include<queue>
 12 #include<vector>
 13 #include<stack>
 14 using namespace std;
 15 typedef bool boolean;
 16 #define INF 0xfffffff
 17 #define smin(a, b) a = min(a, b)
 18 #define smax(a, b) a = max(a, b)
 19 template<typename T>
 20 inline void readInteger(T& u){
 21     char x;
 22     int aFlag = 1;
 23     while(!isdigit((x = getchar())) && x != '-');
 24     if(x == '-'){
 25         x = getchar();
 26         aFlag = -1;
 27     }
 28     for(u = x - '0'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - '0');
 29     ungetc(x, stdin);
 30     u *= aFlag;
 31 }
 32 
 33 typedef class Edge {
 34     public:
 35         int end;
 36         int next;
 37         int w;
 38         Edge(const int end = 0, const int next = 0, const int w = 0):end(end), next(next), w(w) {        }
 39 }Edge;
 40 
 41 typedef class MapManager{
 42     public:
 43         int ce;
 44         int *h;
 45         Edge *edge;
 46         MapManager(){}
 47         MapManager(int points, int limit):ce(0){
 48             h = new int[(const int)(points + 1)];
 49             edge = new Edge[(const int)(limit + 1)];
 50             memset(h, 0, sizeof(int) * (points + 1));
 51         }
 52         inline void addEdge(int from, int end, int w){
 53             edge[++ce] = Edge(end, h[from], w);
 54             h[from] = ce;
 55         }
 56         inline void addDoubleEdge(int from, int end, int w){
 57             addEdge(from, end, w);
 58             addEdge(end, from, w);
 59         }
 60         Edge& operator [](int pos) {
 61             return edge[pos];
 62         }
 63 }MapManager;
 64 #define m_begin(g, i) (g).h[(i)]
 65 
 66 int n, m;
 67 MapManager g;
 68 int* belong;
 69 int maxval = -1;
 70 
 71 inline void init() {
 72     readInteger(n);
 73     readInteger(m);
 74     g = MapManager(n, 2 * m);
 75     belong = new int[(const int)(n + 1)];
 76     for(int i = 1, a, b, w; i <= m; i++) {
 77         readInteger(a);
 78         readInteger(b);
 79         readInteger(w);
 80         g.addDoubleEdge(a, b, w);
 81         smax(maxval, w);
 82     }
 83 }
 84 
 85 boolean dfs(int node, int val, int x) {
 86     if(belong[node] != -1)    return belong[node] == val;
 87     else    belong[node] = val;
 88     for(int i = m_begin(g, node); i != 0; i = g[i].next) {
 89         if(g[i].w <= x)    continue;
 90         int& e = g[i].end;
 91         if(!dfs(e, val ^ 1, x))    return false;
 92     }
 93     return true;
 94 }
 95 
 96 boolean check(int x) {
 97     memset(belong, -1, sizeof(int) * (n + 1));
 98     for(int i = 1; i <= n; i++) {
 99         if(belong[i] == -1)
100             if(!dfs(i, 0, x))    return false;
101     }
102     return true;
103 }
104 
105 inline void solve() {
106     int l = 0, r = maxval;
107     while(l <= r) {
108         int mid = (l + r) >> 1;
109         if(!check(mid))    l = mid + 1;
110         else r = mid - 1;
111     }
112     printf("%d", r + 1);
113 }
114 
115 int main() {
116     freopen("prison.in", "r", stdin);
117     freopen("prison.out", "w", stdout);
118     init();
119     solve();
120     return 0;
121 }


  对于可以使干旱区的每个城市都能够获得水的情况,那么对于湖泊边的某个城市建的蓄水厂,能够提供给干旱区的城市水的集合,是一段连续的区间。这里可以简要地说明一下

  假设上面的命题不成立,就会出现形如上面的情况,那么为了使中间的小圆圈所在的城市可以得到供水,那么一定会有其他的水路和这几条水路交叉,也就是说这个蓄水厂还是可以给小圆圈所在的城市供水,矛盾,所以上面的命题成立。

  于是我们可以通过M次dfs得到假设在第一行第i个城市建蓄水厂所能够供水的区间,当然这样的时间复杂度最极端的情况下是O(m2n)。但是仔细一想,对于一个点(i,j)它可以到达的干旱区城市,那么某个可以到达它的点(i+a,j+b)一定也可以到达它所可以到达的干旱区城市,于是只需要一些dfs从没有被访问的第一行的城市开始搜索,因为每个城市只会被访问一次,所以时间复杂度为O(mn),降回了可以接受的范围。

  得到了这么一堆区间可以干什么?我们是需要选择一些区间使得它们的并集是整个干旱区,于是可以用dp[i]来表示完全覆盖干旱区第1个城市到第j个城市的最小所需的区间,于是又得到了方程:

Code

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cctype>
  4 #include<cstring>
  5 #include<cstdlib>
  6 #include<fstream>
  7 #include<sstream>
  8 #include<algorithm>
  9 #include<map>
 10 #include<set>
 11 #include<queue>
 12 #include<vector>
 13 #include<stack>
 14 using namespace std;
 15 typedef bool boolean;
 16 #define INF 0xfffffff
 17 #define smin(a, b) a = min(a, b)
 18 #define smax(a, b) a = max(a, b)
 19 template<typename T>
 20 inline void readInteger(T& u){
 21     char x;
 22     int aFlag = 1;
 23     while(!isdigit((x = getchar())) && x != '-');
 24     if(x == '-'){
 25         x = getchar();
 26         aFlag = -1;
 27     }
 28     for(u = x - '0'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - '0');
 29     ungetc(x, stdin);
 30     u *= aFlag;
 31 }
 32 
 33 typedef class Point {
 34     public:
 35         int x, y;
 36         int l, r;
 37         
 38         Point(const int x = 0, const int y = 0, const int l = 0, const int r = 0):x(x), y(y), l(l), r(r) {        }
 39         
 40         boolean mergable(Point& b) {
 41             if(b.l < l)    return true;
 42             if(b.r > r)    return true;
 43             return false;
 44         }
 45         void merge(Point& b) {
 46             smin(l, b.l);
 47             smax(r, b.r);
 48         }
 49 }Point;
 50 
 51 int n, m;
 52 int mat[505][505];
 53 
 54 inline void init() {
 55     readInteger(n);
 56     readInteger(m);
 57     for(int i = 1; i <= n; i++) {
 58         for(int j = 1; j <= m; j++) {
 59             readInteger(mat[i][j]);
 60         }
 61     }
 62 }
 63 
 64 const int mover[2][4] = {{0, 1, 0, -1}, {1, 0, -1, 0}};
 65 
 66 Point f[505][505];
 67 boolean vis[505][505];
 68 Point dfs(int x, int y) {
 69     if(vis[x][y])    return f[x][y];
 70     vis[x][y] = true;
 71     f[x][y] = Point(x, y, 999, 0);
 72     if(x == n)    f[x][y] = Point(x, y, y, y);
 73     for(int k = 0; k < 4; k++) {
 74         int x1 = x + mover[0][k], y1 = y + mover[1][k];
 75         if(x1 < 1 || x1 > n)    continue;
 76         if(y1 < 1 || y1 > m)    continue;
 77         if(mat[x1][y1] < mat[x][y]) {
 78             Point e = dfs(x1, y1);
 79             f[x][y].merge(e);
 80         }
 81     }
 82     return f[x][y];
 83 }
 84 
 85 void dfs1(int x, int y) {
 86     if(vis[x][y])    return;
 87     vis[x][y] = true;
 88     for(int k = 0; k < 4; k++) {
 89         int x1 = x + mover[0][k], y1 = y + mover[1][k];
 90         if(x1 < 1 || x1 > n)    continue;
 91         if(y1 < 1 || y1 > m)    continue;
 92         if(mat[x1][y1] < mat[x][y]) {
 93             Point e = dfs(x1, y1);
 94             f[x][y].merge(e);
 95         }
 96     }
 97 }
 98 
 99 int *dp;
100 inline void solve() {
101     memset(vis, false, sizeof(vis));
102     for(int i = 1; i <= m; i++)
103         if(!vis[1][i])
104             dfs(1, i);
105     dp = new int[(const int)(m + 2)];
106     memset(dp, 0, sizeof(int) * (m + 2));
107     for(int i = 1; i <= m; i++)
108         if(f[1][i].l < 999)
109             dp[f[1][i].l] += 1, dp[f[1][i].r + 1] -= 1;
110     int unget = 0;
111     for(int i = 1; i <= m; i++) {
112         if(!vis[n][i])
113             unget++;
114     }
115     if(unget > 1) {
116         printf("0
%d", unget);
117         return;
118     }
119     memset(dp, 0x7f, sizeof(int) * (m + 2));
120     dp[0] = 0;
121     for(int i = 1; i <= m; i++) {
122         for(int j = 1; j <= m; j++) {
123             if(f[1][j].l > i || f[1][j].r < i)    continue;
124             smin(dp[i], dp[f[1][j].l - 1] + 1);
125         }
126     }
127     printf("1
%d", dp[m]);
128 }
129 
130 int main() {
131     freopen("flow.in", "r", stdin);
132     freopen("flow.out", "w", stdout);
133     init();
134     solve();
135     return 0;
136 }
原文地址:https://www.cnblogs.com/yyf0309/p/6413588.html