Codeforces #277.5 (Div.2 A~F)

比赛地址

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");
		else
			puts("-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;
		else
			R = 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;
}

小记

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

你可能感兴趣的:(排序,dp,搜索,贪心,二分)