Codeforces #277.5 (Div.2 A~F)

来源:互联网 发布:淘宝上二手房翻新 编辑:程序博客网 时间:2024/05/22 16:43

比赛地址

Codeforces Round #277.5(Div.2)


489A. SwapSort

题意:

给定一个长度为n的序列,每次可以选择序列中的两个元素交换位置,求任意一组交换次数不超过n次的方案,使交换后的序列是按升序排列的。

n <= 3000。

题解:

我们可以使用选择排序来解决这个问题。

虽然选择排序会进行O(n^2)次比较,但是它具有一个很好的性质,我们可以使它的交换次数是O(n)的。

时间复杂度O(n^2)。

代码:

#include <cstdio>#include <algorithm>using namespace std;int n, a[3333], ans[3333][2], tot;int main(){scanf("%d", &n);for(int i = 0; i < n; ++i)scanf("%d", a + i);for(int i = 0; i < n; ++i){int tmp = i;for(int j = i + 1; j < n; ++j)if(a[j] < a[tmp])tmp = j;if(tmp != i){ans[tot][0] = i;ans[tot][1] = tmp;++tot;swap(a[i], a[tmp]);}}printf("%d\n", tot);for(int i = 0; i < tot; ++i)printf("%d %d\n", ans[i][0], ans[i][1]);return 0;}

489B. BerSU Ball

题意:

有n个男孩、m个女孩参加舞会,每个人有一个舞蹈能力,我们约定一对男女能够一起跳舞,必须满足两人的舞蹈能力之差的绝对值不超过1,求最多能组成多少对男女跳舞。

n, m <= 100。

题解:

我们可以将男孩、女孩的舞蹈能力分别排序,这样可以使得任意一个男生可以共舞的女生是连续的一段,任意一个女生可以共舞的男生是连续的一段。

不难想到用f[i, j]表示前i个男生和前j个女生能成对的数量,将问题转化为dp。时间复杂度O(nm)。

排序后也可以用贪心的算法。时间复杂度O(nlogn)。

代码:

#include <cstdio>#include <algorithm>using namespace std;int n, m, a[233], b[233], f[233][233];int main(){scanf("%d", &n);for(int i = 1; i <= n; ++i)scanf("%d", a + i);scanf("%d", &m);for(int i = 1; i <= m; ++i)scanf("%d", b + i);sort(a + 1, a + n + 1);sort(b + 1, b + m + 1);for(int i = 1; i <= n; ++i)for(int j = 1; j <= m; ++j){f[i][j] = max(f[i][j - 1], f[i - 1][j]);if(abs(a[i] - b[j]) <= 1)f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);}printf("%d\n", f[n][m]);return 0;}

489C. Give Length and Sum of Digits...

题意:

限定一个数的位数为m,各数位数字之和为s,求这个数可能的最小值和最大值,不存在则输出-1 -1。

m <= 100。

题解:

无解的情况:一是每个位上都是9也无法满足s,即s > 9*m;二是当m>1时s不足1,因为一个m位数最小是10^(m-1)。

这个数的最高位最少是1,其他位最少是0,可以利用贪心的思想构造出最优解,求最小值是尽量用低位去满足s,求最大值则是从高位。

时间复杂度O(m)。

代码:

#include <cstdio>int m, s, str1[233], str2[233], cnt1, cnt2;int main(){scanf("%d%d", &m, &s);if(s == 0){if(m == 1)puts("0 0");elseputs("-1 -1");return 0;}if(s > 9 * m){puts("-1 -1");return 0;}str1[m - 1] = str2[m - 1] = cnt1 = cnt2 = 1;for(int i = 0; i < m && cnt1 < s; ++i)if(cnt1 + 9 - str1[i] < s){cnt1 += 9 - str1[i];str1[i] = 9;}else{str1[i] += s - cnt1;cnt1 = s;}for(int i = m - 1; i >= 0 && cnt2 < s; --i)if(cnt2 + 9 - str2[i] < s){cnt2 += 9 - str2[i];str2[i] = 9;}else{str2[i] += s - cnt2;cnt2 = s;}for(int i = m - 1; i >= 0; --i)putchar('0' + str1[i]);putchar(' ');for(int i = m - 1; i >= 0; --i)putchar('0' + str2[i]);putchar('\n');return 0;}

489D. Unbearable Controversy of Being

题意:

给定n个点,m条边的有向图,求满足条件"b≠d, a→b→d, a→c→d"的本质不同的四元组(a, b, c, d)的个数,其中(a, b, c, d)与(a, c, b, d)本质相同。

n <= 3000, m <= 30000。

题解:

算出每个点走两步到达另一个点的方案数,通过组合数学知识算出满足条件的(a, *, *, d)的个数。

关于走两步,可以搜索两步,因为每个点的搜索通过的边数量是O(m)的,所以整体算法是O(nm)的。

时间复杂度O(nm)。

代码:

#include <map>#include <cstdio>#include <algorithm>using namespace std;int n, m;map<int, int> g1[3010];int g2[3010];long long ans;int main(){scanf("%d%d", &n, &m);for(int i = 1, u ,v; i <= m; ++i){scanf("%d%d", &u, &v);++g1[u][v];}for(int i = 1; i <= n; ++i){memset(g2, 0, sizeof g2);for(map<int, int>::iterator it1 = g1[i].begin(), jt1 = g1[i].end(); it1 != jt1; ++it1){int j = it1 -> first;for(map<int, int>::iterator it2 = g1[j].begin(), jt2 = g1[j].end(); it2 != jt2; ++it2){int k = it2 -> first;g2[k] += (it1 -> second) * (it2 -> second);}}for(int j = 1; j <= n; ++j)if(i != j && g2[j] >= 2)ans += (long long)g2[j] * (g2[j] - 1) / 2;}printf("%I64d\n", ans);return 0;}

489E. Hiking

题意:

数轴上有n个点,依次标号1~n,每个点有两个属性,坐标x_i和到达该点可增加的好感度b_i。

规定如果从一个点到另一个点的路程是r,那么这段路程会增加\sqrt{|r - l|}点不适感,其中l为已给定的常数。

求一个运动路线使得从原点开始到达第n个点后的不适感与好感度的比值最小,只能从编号小的点向编号大的点走,保证走的方向一致。

n <= 1000, l <= 10 ^ 5, x_i <= 10 ^ 6。

题解:

经典的分数规划问题。

不妨设最优解的不适感与好感度的比值为k,走过的点数为m,每次增加不适感为p_i,好感度q_i,则\sum_{i = 1} ^ {m} {p_i} /  \sum_{i = 1} ^ {m} {q_i} = k,那么可以转化为\sum_{i = 1} ^ {m} {p_i - k * q_i} = 0。如果给每种可能的路径定义权值为{p_i - k * q_i},则这个式子可以理解为从0点到第n个点的路径上每条边的权值和。则对于给定的答案,我们可以通过计算最短路算出答案是否合法。

如果存在最短路的值不小于0,则该解及比它大的k可能可行。据此可以二分答案k。

时间复杂度O(nlog\sqrt{nx_i})。

代码:

#include <stack>#include <cstdio>#include <algorithm>using namespace std;const double eps = 1e-10;int n, l, x[1010], b[1010], p[1010];bool vis[1010];double f[1010], L, R, M;bool judge(){for(int i = 1; i <= n; ++i){f[i] = sqrt(abs(x[i] - l));p[i] = 0;for(int j = 1; j < i; ++j)if(f[i] > f[j] + sqrt(abs(x[i] - x[j] - l))){f[i] = f[j] + sqrt(abs(x[i] - x[j] - l));p[i] = j;}f[i] -= M * b[i];}return f[n] >= 0;}int main(){scanf("%d%d", &n, &l);for(int i = 1; i <= n; ++i)scanf("%d%d", x + i, b + i);for(M = 1; judge(); M *= 2);L = (int)M / 2;R = M;while(R - L >= eps){M = (L + R) / 2;if(judge())L = M;elseR = M;}stack<int> s;for(int i = n; i; i = p[i])s.push(i);while(!s.empty()){printf("%d ", s.top());s.pop();}putchar('\n');return 0;}

489F. Special Matrices

题意:

限定一种n*n的01矩阵,它的每行或每列数字之和为2。

现给定一个这种01矩阵的前m行,求这种矩阵有多少种可能的形态,答案可能很大,对一个数取模。

2 <= n <= 500, 0 <= m <= n, 2 <= mod <= 10 ^ 9。

题解:

我们可以统计出有多少个列还差两个1,有多少个列还差一个1,设f[i, j]表示剩i个列差两个1,剩j个列差一个1的方案数,则可以分三种情况讨论填1~2个1之后的状态,利用组合数学推出f[i, j]。

时间复杂度O(n^2)。

代码:

#include <cstdio>int n, m, mod, r[555], cnt[2], f[555][555];char str[555];int main(){scanf("%d%d%d", &n, &m, &mod);for(int i = 0; i < m; ++i){scanf("%s", str);for(int j = 0; j < n; ++j)if(str[j] == '1')++r[j];}for(int i = 0; i < n; ++i)++cnt[r[i]];f[0][0] = 1;for(int i = 0; i <= cnt[0]; ++i)for(int j = 0; j <= n; ++j){if(i >= 2 && j + 2 <= n){f[i][j] += (long long)i * (i - 1) / 2 * f[i - 2][j + 2] % mod;if(f[i][j] >= mod)f[i][j] -= mod;}if(j >= 2){f[i][j] += (long long)j * (j - 1) / 2 * f[i][j - 2] % mod;if(f[i][j] >= mod)f[i][j] -= mod;}if(i && j){f[i][j] += (long long)i * j * f[i - 1][j] % mod;if(f[i][j] >= mod)f[i][j] -= mod;}}printf("%d\n", f[cnt[0]][cnt[1]]);return 0;}

小记

考试应该戒骄戒躁;学习算法与实践缺一不可。

0 0
原创粉丝点击