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\n%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\n%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 }