A. Pasha and Pixels (水题 暴力)
题意:
有一个n×m的矩阵,最开始所有的格子都是白色的。
一个人按照某种顺序将格子涂成黑的,如果某个格子已经是黑的则忽略。在涂色的过程中如果出现了2×2的全黑小方阵,则游戏失败。
输出游戏是在哪一步失败的,或者没有失败。
分析:
直接用一个矩阵模拟即可,每次给一个格子涂完色,判断一下包含这个格子的四个2×2方阵是否全威黑色,注意坐标不要出界。
1 #include <iostream> 2 #include <cstdio> 3 4 const int maxn = 1000 + 10; 5 int dx[] = {-1, -1, 1, 1}; 6 int dy[] = {1, -1, -1 ,1}; 7 int map[maxn][maxn]; 8 int n, m, k; 9 10 bool inside(int x, int y) { return x>=1 && x<=n && y>=1 && y<=m; } 11 12 bool judge(int x, int y) 13 { 14 for(int i = 0; i < 4; ++i) 15 { 16 int xx = x + dx[i]; 17 int yy = y + dy[i]; 18 if(inside(xx, yy) && map[xx][yy] 19 && inside(x, yy) && map[x][yy] 20 && inside(xx, y) && map[xx][y]) 21 return true; 22 } 23 24 return false; 25 } 26 27 int main() 28 { 29 //freopen("in.txt", "r", stdin); 30 31 scanf("%d%d%d", &n, &m, &k); 32 33 for(int i = 1; i <= k; ++i) 34 { 35 int x, y; 36 scanf("%d%d", &x, &y); 37 map[x][y] = 1; 38 if(judge(x, y)) { printf("%d", i); return 0; } 39 } 40 41 puts("0"); 42 43 return 0; 44 }
B. Anton and currency you all know (贪心)
题意:
给出一个奇数,交换两个不同位置的数字,求能得到的最大的偶数。
分析:
因为要把奇数变成偶数,所以要用一种偶数数字与个位数字交换。
于是乎,我枚举所有可能得到的偶数,然后维护一个最大值,其中是用字典序来比较的。果断TLE了
我们还是从数字或者字典序的大小比较来入手。
从左往右扫描,如果某次交换使数字变大,则它一定是答案。因为这个是能使最高位变大的数。
如果没有一种交换使数字变大的话,那就从右往左扫描,找到那个使最低位变小的数。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 const int maxn = 100000 + 10; 6 char s[maxn]; 7 8 int main() 9 { 10 scanf("%s", s); 11 int l = strlen(s); 12 int up = -1, low = -1, pos; //最高位变大的位置,最低位变小的位置以及最后要交换的位置 13 for(int i = 0; i < l-1; i++) 14 if((s[i]-'0')%2 == 0 && s[i] < s[l-1]) { up = i; break; } 15 if(up == -1) 16 for(int i = l-1; i >= 0; --i) 17 if((s[i]-'0')%2 == 0 && s[i] > s[l-1]) { low = i; break; } 18 19 if(up == -1 && low == -1) { puts("-1"); return 0; } 20 else if(up != -1) pos = up; 21 else pos = low; 22 std::swap(s[pos], s[l-1]); 23 24 printf("%s ", s); 25 26 return 0; 27 }
C. Anya and Ghosts (贪心)
题意:
有m只鬼要来Anya的房间,为了抵御它们,Anya要保证在鬼到来的那一秒保证房间里至少有r只蜡烛在燃烧。
点蜡烛都是在整秒时刻点(时间可以是负的),而且需要一秒的时间,每秒只能点一只蜡烛。点亮后每只蜡烛会持续燃烧t秒后熄灭。
求所需要的最少蜡烛数。
分析:
首先t<r时,是不会出现r只蜡烛同时燃烧的。因为后面的蜡烛还没点着,前面的蜡烛就熄灭了。
t≥r时,一定是有解的。因为如果我们每秒都点亮一根蜡烛,就会一直都满足条件。现在要求最小值,贪心即可。
只要保证在鬼来的那一秒有r根蜡烛在燃烧就好了,记录下每个时刻正在燃烧的蜡烛数。在鬼来的那一秒的之前的时间“补全”所需要的蜡烛就可以了。
1 #include <cstdio> 2 3 const int maxn = 1000; 4 int m, t, r, ans; 5 int time[maxn], num[maxn]; 6 7 int main() 8 { 9 //freopen("in.txt", "r", stdin); 10 11 scanf("%d%d%d", &m, &t, &r); 12 for(int i = 0; i < m; ++i) scanf("%d", &time[i]); 13 14 if(t < r) { puts("-1"); return 0; } 15 for(int i = 0; i < m; ++i) 16 { 17 int need = r - num[time[i]]; //所需要的蜡烛数 18 if(need > 0) 19 { 20 ans += need; 21 for(int j = 0; j <= t - need; ++j) num[time[i] + j] += need; 22 for(int j = 1; j < need; ++j) num[time[i]+t-need+j] += need - j; 23 } 24 } 25 26 printf("%d ", ans); 27 28 return 0; 29 }
D. Tanya and Password (图论、欧拉路径)
题意:
有一个长度为n+2的字符串,现在只知道它的n个长度为3的子串。问这n个子串能不能拼成一个长为n+2的串。
分析:
将每个长度为3的子串可以看成一条有向边,起点是前两个字符,终点是后两个字符。两个子串可以连接当且仅当一个子串的后两个字符和另一个子串的前两个字符相等,比如abc与bcd连接后得到abcd.
这n个子串可以连接起来,就等价于找到一条欧拉路径。存在欧拉路径的条件就是:
- 所有顶点出度等于入度
- 只有一个顶点出度比入度大一,只有一个顶点入度比出度大一,其他顶点均出度等于入度
为了方便表示,我们要给这些字符编上号,以代码中为例,a~z是0~25号,A~Z是26~51号,0~9是52~61号。
这样一个顶点(两个字符)就可以看做一个62进制的两位数。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <cmath> 5 #include <string> 6 #include <algorithm> 7 using namespace std; 8 9 const int maxn = 62 * 62; 10 vector<int> G[maxn]; 11 int n, cnt[maxn], in[maxn], out[maxn]; 12 13 int charNum(char c) 14 {//将字符转化为对应编号 15 if(c >= 'a' && c <= 'z') return c - 'a'; 16 if(c >= 'A' && c <= 'Z') return c - 'A' + 26; 17 return c - '0' + 52; 18 } 19 20 int VertexNum(char a, char b) 21 {//计算顶点对应的编号 22 return charNum(a) * 62 + charNum(b); 23 } 24 25 char ToChar(int x) 26 {//将编号转化为字符 27 if(x < 26) return 'a' + x; 28 if(x < 52) return 'A' + x - 26; 29 return '0' + x - 52; 30 } 31 32 void Euler(int v, string& ans) 33 {//找欧拉回路,并保存在ans中 34 while(cnt[v] < G[v].size()) Euler(G[v][cnt[v]++], ans); 35 ans += ToChar(v % 62); 36 } 37 38 int main() 39 { 40 //freopen("in.txt", "r", stdin); 41 42 scanf("%d", &n); 43 for(int i = 0; i < n; ++i) 44 { 45 char sub[5]; 46 scanf("%s", sub); 47 int u = VertexNum(sub[0], sub[1]); 48 int v = VertexNum(sub[1], sub[2]); 49 G[u].push_back(v); 50 out[u]++; in[v]++; 51 } 52 53 int start = -1; 54 for(int v = 0; v < maxn; ++v) 55 { 56 if(abs(in[v] - out[v]) > 1) { puts("NO"); return 0; } 57 if(out[v] > in[v]) 58 { 59 if(start != -1) { puts("NO"); return 0; } 60 start = v; 61 } 62 } 63 if(start == -1) for(int v = 0; v < maxn; ++v) if(!G[v].empty()) { start = v; break; } 64 65 string ans = ""; 66 Euler(start, ans); 67 ans += ToChar(start / 62); 68 reverse(ans.begin(), ans.end()); 69 70 if(ans.size() != n + 2) puts("NO"); 71 else { puts("YES"); cout << ans << " "; } 72 73 return 0; 74 }
E. Arthur and Brackets (贪心)
CF上给这道题打了dp和greedy两个标签,应该是两种做法都可以吧。下面说贪心的做法。
题意:
有一些配好对的括号,现在已知第i对括号,左右括号的距离在[Li, Ri]区间中。按照左括号出现的顺序编号。
输出原括号序列。
分析:
因为括号是以栈的形式配对的,所以我们将这些区间也以栈的形式存储。
假设第i对括号的左括号在位置p,则右括号只能在[p+Li, p+Ri]这个区间中。
每放一个左括号,就将右括号对应的区间入栈。
贪心的办法是,如果当前位置位于栈顶区间的范围内,则尽早入栈。
贪心的理由是:因为早点使栈顶的括号配对,就有更大的机会使栈顶的第二对括号配上对。
1 #include <cstdio> 2 #include <stack> 3 #include <utility> 4 using namespace std; 5 6 const int maxn = 600 + 10; 7 int n, L[maxn], R[maxn]; 8 char s[maxn * 2]; 9 10 int main() 11 { 12 scanf("%d", &n); 13 for(int i = 0; i < n; ++i) scanf("%d%d", &L[i], &R[i]); 14 int p = 0; //当前字符串的末位置 15 stack<pair<int, int> > st; 16 for(int i = 0; i < n; ++i) 17 { 18 st.push(make_pair(L[i]+p, R[i]+p)); 19 s[p++] = '('; 20 while(!st.empty() && st.top().first <= p && p <= st.top().second) 21 { 22 s[p++] = ')'; 23 st.pop(); 24 } 25 if(!st.empty() && p > st.top().second) { puts("IMPOSSIBLE"); return 0; }//已经超过栈顶区间范围,永远也配不上对 26 } 27 28 if(!st.empty()) { puts("IMPOSSIBLE"); return 0; } 29 30 puts(s); 31 32 return 0; 33 }