Codeforces Round #302 (Div. 2)
A. Set of Strings
题意:能否把一个字符串划分为n段,且每段第一个字母都不相同?
思路:判断字符串中出现的字符种数,然后划分即可。
1 #include<iostream> 2 #include<set> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 char s[110]; 7 set<char>st; 8 int main() 9 { 10 int n; 11 scanf("%d%s", &n,s); 12 int len = strlen(s); 13 for (int i = 0; i < len; i++) 14 { 15 if (!st.count(s[i]))st.insert(s[i]); 16 } 17 if (st.size() < n) printf("NO\n"); 18 else 19 { 20 st.clear(); 21 printf("YES\n"); 22 int cur = 0; 23 while (n--) 24 { 25 st.insert(s[cur]); 26 while (cur<len&&st.count(s[cur])) 27 { 28 printf("%c", s[cur++]); 29 } 30 if (n == 0) 31 { 32 while(cur<len) printf("%c", s[cur++]); 33 } 34 printf("\n"); 35 } 36 } 37 return 0; 38 }
B. Sea and Islands
题意:n*n的网格里都是水,现在需要填沙造出k个陆地,两两之间有边相邻的看成一片陆地。
思路:讨论奇偶。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int main() 5 { 6 int n, k; 7 scanf("%d%d", &n, &k); 8 int maxs = n / 2 * n + n % 2 * (n + 1) / 2; 9 if (k > maxs)printf("NO\n"); 10 else 11 { 12 printf("YES\n"); 13 for (int i = 0; i < n; i++) 14 { 15 int st; 16 if (i % 2==0) st = 0; 17 else st = 1; 18 for (int j=0; j < n; j++) 19 { 20 if ((j - st) % 2 == 0&&k>0) printf("L"),k--; 21 else printf("S"); 22 } 23 printf("\n"); 24 } 25 } 26 return 0; 27 }
C. Writing Code
题意:有n个程序员,现在需要合作完成m行的代码,要求最多只有b个bug,求总方案数?
思路:dp[i][j][k]:表示前i个程序员一起写j行代码bug数为k的方案数.采用滚动数组优化。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn = 510; 5 const int maxm = 510; 6 const int maxb = 510; 7 int dp[2][maxm][maxb];//[i][j][k]表示前i个程序员一起写j行代码bug数为k的方案数 8 int bugs[maxn]; 9 int main() 10 { 11 int n, m, b, mod; 12 scanf("%d%d%d%d", &n, &m, &b, &mod); 13 for (int i = 1; i <= n; i++) scanf("%d", bugs + i); 14 dp[0][0][0] = dp[1][0][0] = 1; 15 int pre, now; 16 for (int i = 1; i <= n; i++) 17 { 18 for (int j = 1; j <= m; j++) 19 { 20 for (int k =0; k <= b; k++) 21 { 22 pre = ((i - 1) & 1),now=(i&1); 23 if (k >= bugs[i]) dp[now][j][k] = (dp[pre][j][k] + dp[now][j - 1][k - bugs[i]]) % mod; 24 else dp[now][j][k] = dp[pre][j][k]; 25 } 26 } 27 } 28 int ans = 0; 29 for (int i = 0; i <= b; i++) ans = (ans + dp[now][m][i]) % mod; 30 printf("%d\n",ans); 31 return 0; 32 }
D. Destroying Roads
题意:有n个点,m条无向边。在保证s1到t1不超过l1小时、s2到t2不超过l2小时(每走一条边花费1小时)的情况下,求最多可以删去多少条边?
思路:求出每两点之间的最短距离,然后2层循环枚举重复的路径。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<queue> 5 #include<cstring> 6 using namespace std; 7 const int maxn = 3010; 8 struct edge 9 { 10 int next, to; 11 edge(int tt=0,int nn=0):to(tt),next(nn){} 12 }; 13 edge Edge[maxn*maxn]; 14 int Head[maxn], totedge; 15 void addedge(int from, int to) 16 { 17 Edge[totedge] = edge(to, Head[from]); 18 Head[from] = totedge++; 19 Edge[totedge] = edge(from, Head[to]); 20 Head[to] = totedge++; 21 } 22 int dis[maxn][maxn]; 23 bool vis[maxn]; 24 const int INF = 0x3f3f3f3f; 25 int n, m; 26 void SPFA(int st) 27 { 28 for (int i = 0; i <= n; i++) dis[st][i] = INF; 29 dis[st][st] = 0; 30 vis[st] = true; 31 queue<int>q; 32 q.push(st); 33 while (!q.empty()) 34 { 35 int u = q.front(); 36 q.pop(); 37 vis[u] = false; 38 for (int i = Head[u]; i != -1; i = Edge[i].next) 39 { 40 int v = Edge[i].to; 41 if (dis[st][v] > dis[st][u] + 1) 42 { 43 dis[st][v] = dis[st][u] + 1; 44 if (!vis[v]) 45 { 46 vis[v] = true; 47 q.push(v); 48 } 49 } 50 } 51 } 52 } 53 int main() 54 { 55 scanf("%d%d", &n, &m); 56 memset(Head, -1, sizeof(Head)); 57 totedge = 0; 58 for (int i = 1; i <= m; i++) 59 { 60 int u, v; 61 scanf("%d%d", &u, &v); 62 addedge(u, v); 63 } 64 int s1, d1, l1, s2, d2, l2; 65 scanf("%d%d%d%d%d%d", &s1, &d1, &l1, &s2, &d2, &l2); 66 for (int i = 1; i <= n; i++) SPFA(i); 67 if (dis[s1][d1] > l1 || dis[s2][d2] > l2) printf("-1\n"); 68 else 69 { 70 int minnum = dis[s1][d1] + dis[s2][d2]; 71 for (int i = 1; i <= n; i++) 72 { 73 for (int j = 1; j <= i; j++) 74 { 75 if (dis[s1][i] + dis[i][j] + dis[j][d1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][d2] <= l2) 76 { 77 minnum = min(minnum, dis[s1][i] + dis[i][j] + dis[j][d1] + dis[s2][i] + dis[j][d2]); 78 } 79 if (dis[s1][i] + dis[i][j] + dis[j][d1] <= l1 && dis[s2][j] + dis[j][i] + dis[i][d2] <= l2) 80 { 81 minnum = min(minnum, dis[s1][i] + dis[i][j] + dis[j][d1] + dis[s2][j] + dis[i][d2]); 82 } 83 if (dis[s1][j] + dis[j][i] + dis[i][d1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][d2] <= l2) 84 { 85 minnum = min(minnum, dis[s1][j] + dis[j][i] + dis[i][d1] + dis[s2][i] + dis[j][d2]); 86 } 87 if (dis[s1][j] + dis[j][i] + dis[i][d1] <= l1 && dis[s2][j] + dis[j][i] + dis[i][d2] <= l2) 88 { 89 minnum = min(minnum, dis[s1][j] + dis[j][i] + dis[i][d1] + dis[s2][j] + dis[i][d2]); 90 } 91 } 92 } 93 printf("%d\n", m - minnum); 94 } 95 return 0; 96 }
E. Remembering Strings
题意:有n个字符串,每个字符串m位,要使得每个字符串存在一个位置i上的字符唯一(其他字符串该位上的字符与该字符串不同,称为易记字符串),你可以将某个字符串某位替换成任意字符,给出每个字符串每位替换的代价,求最小代价?
思路:将n个字符串各自是否为易记标记为0/1,则我们可以进行状态压缩,对每个当前的状态,找到最低位的0(表示该字符串进行转换,其余高位的0无需转换),按照以下策略进行替换:如果该位唯一,则dp[i | (1 << j)] = dp[i],否则考虑2种方案:第一种方案,直接让该位字符唯一——dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + cost[j][k]);第二种方案,找到所有和该字符串该位字符一样的所有字符串,除去代价最大的,其他字符串进行转换——dp[i | tmp] = min(dp[i | tmp], dp[i] + sumc - maxc)。
初始化为-1时需要考虑当前状态在此前是否已被转换到,没有则跳过;初始化为INF则不用考虑。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 char str[25][25]; 7 int cost[25][25]; 8 const int maxc = 1 << 20;//最大状态数 9 int dp[maxc]; 10 int main() 11 { 12 int n, m; 13 scanf("%d%d", &n, &m); 14 for (int i = 0; i < n; i++) scanf("%s", str + i); 15 for (int i = 0; i < n; i++) 16 { 17 for (int j = 0; j < m; j++) scanf("%d", &cost[i][j]); 18 } 19 int total = (1 << n) - 1;//最终状态为111...11,n个1表示n个字符串都为易记 20 memset(dp, -1, sizeof(dp)); 21 dp[0] = 0; 22 for (int i = 0; i <total; i++)//枚举状态 23 { 24 if (dp[i] == -1) continue;//该状态没有被转移到,跳过 25 int j = 0; 26 while (((i >> j) & 1) == 1) j++;//找到该状态中为非易记的字符串进行状态转移 27 for (int k = 0; k < m; k++) 28 {//让该字符串第k位的字符唯一 29 int cnt = 0, sumc = 0, maxc = 0, tmp = 0; 30 for (int z = 0; z < n; z++) 31 { 32 if (str[z][k] == str[j][k]) 33 { 34 cnt++; 35 sumc += cost[z][k];//总代价 36 maxc = max(maxc, cost[z][k]);//最大代价 37 tmp |= (1 << z);//所有和该字符串该位一样的字符串为易记时的状态 38 } 39 } 40 if (cnt == 1)//如果该位字符本就唯一 41 { 42 if (dp[i | (1 << j)] == -1)dp[i | (1 << j)] = dp[i]; 43 else dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i]); 44 } 45 else//否则 46 { 47 //第一种方案,直接让该位字符唯一 48 if (dp[i | (1 << j)] == -1) dp[i | (1 << j)] = dp[i] + cost[j][k]; 49 else dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + cost[j][k]); 50 //第二种方案,找到所有和该字符串该位字符一样的所有字符串,除去代价最大的,其他字符串进行转换 51 if (dp[i | tmp] == -1) dp[i | tmp] = dp[i] + sumc - maxc; 52 else dp[i | tmp] = min(dp[i | tmp], dp[i] + sumc - maxc); 53 } 54 } 55 } 56 printf("%d\n", dp[total]); 57 return 0; 58 }
出处:http://www.cnblogs.com/ivan-count/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。