Codeforces Round #642 (Div. 3) 题解

传送门

 

A. Most Unstable Array

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
using namespace std;
const int INF = 0x3f3f3f3f;
 
 
int main() {
	int t; cin >> t;
	while (t--) {
		int n, m;
		cin >> n >> m;
		if (n >= 3)
			cout << 2 * m << endl;
		else if (n == 2)
			cout << m << endl;
		else
			cout << 0 << endl;
	}
 
}

 

B. Two Arrays And Swaps

题意:

给你两个长度为n的数组a,b,每一次操作可以交换a,b数组中的一对数字,一共可以交换小于等于k次

题解:

a的前k小交换b的前k大,注意交换的时候a数组的元素必须小于b数组的元素

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 33;
int a[maxn], b[maxn];

int main() {
	int t; cin >> t;
	while (t--) {
		int n, k;
		cin >> n >> k;
		for (int i = 1; i <= n; i++)
			scanf("%d", a + i);
		for (int i = 1; i <= n; i++)
			scanf("%d", b + i);
		sort(a + 1, a + n + 1);
		sort(b + 1, b + n + 1);
		int sum = 0;
		for (int i = k + 1; i <= n; i++)
			sum += a[i];
		for (int i = 1; i <= k; i++) {
			if (a[i] > b[n - i + 1])
				sum += a[i];
			else
				sum += b[n - i + 1];
		}
		cout << sum << endl;
	}
}

 

C. Board Moves

题意:有n*n个格子,每个格子上有一个元素,每一次操作可以将一个格子移动到它左上,左下,右上,右下,上,左,下,右八个格子,问最少需要多少次才能够将所有格子上的元素移到同一个格子,n一定是奇数

题解:首先很明显可以移动到最中间的格子。然后这道题的答案可以全部先预处理出来然后一次作答,因为n=x的答案可以从n=x-2的答案中推得。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<algorithm>
#include<stack>
#define ll long long int
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 5e5 + 7;
ll ans[maxn];
int main() {
	int t; cin >> t;
	ans[1] = 0;
	for (ll i = 3; i <= 5e5; i += 2) {
		// (i/2)表示当前层的数字
		ans[i] = ans[i - 2] + 4 *(i - 1) * (i / 2);
	}
	while (t--) {
		int n; cin >> n;
		cout << ans[n] << endl;
	}
	return 0;
}

 

D. Constructing the Array

题意:

好难翻译不翻了。。

题解:

优先队列,长度长的先处理

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<algorithm>
#include<stack>
#define ll long long 
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
int a[maxn], val;
struct node {
	int l, r, val;
	bool operator< (const node& e)const {
		return val == e.val ? l > e.l : val < e.val;
	}
	bool operator> (const node& e)const {
		return val == e.val ? l > e.l : val > e.val;
	}
};
priority_queue<node, vector<node>, less<node> >Q;
int main() {
	int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		val = 0;
		Q.push(node{ 1,n,n });
		node now;
		while (!Q.empty()) {
			now = Q.top();
			Q.pop();
			int mid = now.l + now.r >> 1;
			a[mid] = ++val;
			if (now.val == 2)
				Q.push(node{ mid + 1,now.r,1 });
			else if (now.val > 2) {
				Q.push(node{ now.l,mid - 1,mid - now.l });
				Q.push(node{ mid + 1,now.r,now.r - mid });
			}
		}
		for (int i = 1; i <= n; i++)
			printf("%d ", a[i]);
		putchar('\n');
	}
	return 0;
}

 

E. K-periodic Garland

题意:

给一串长度为n的串,里面只有0和1,每次操作可以将某一位的0变成1或者1变成0 ,操作的最后需要串里面两个1之间有k-1个0,问最少需要操作多少次

题解:

对于某一位都只有两种选择,填0或者填1,设

dp[i][0]:第i位填0,前面都合法,最小需要的操作数

dp[i][1]:第i位填1,前面都合法,最小需要的操作数

因为本题对于 0001000这样的串也算是合法的,所以对于每一位都可以考虑当前位为1,然后前面全是0

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<algorithm>
#include<stack>
#define ll long long 
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 7;
int dp[maxn][2];
int sum[maxn];
char s[maxn];
int main() {
	int t; cin >> t;
	int cnt = 0;
	while (t--) {
		int n, k;
		cin >> n >> k;
		cin >> (s + 1);
		for (int i = 1; i <= n; i++) {
			if (s[i] == '1')
				sum[i] = sum[i - 1] + 1;
			else
				sum[i] = sum[i - 1];
		}

		for (int i = 1; i <= n; i++) {
			int las = max(0, i - k);
			//当前位是0
			dp[i][0] = min(dp[i - 1][1], dp[i - 1][0]) + (s[i] == '1');
			//当前位是1,可以考虑前面都为0,或者k个之前是1
			dp[i][1] = min(dp[las][1] + sum[i - 1] - sum[las], sum[i - 1]) + (s[i] == '0');
		}
		cout << min(dp[n][0], dp[n][1]) << endl;
	}
}

 

F. Decreasing Heights

题意:n*m的地图上每个格子都有它的高度a , 对于第i,j格,你可以向右边走或者下面走,还有另外一个限制条件,每一次只能向比当前高度大1的格子走。你可以进行操作,每次操作将一个格子的高度下降1,问最少需要进行多少次操作可以使你从左上角的格子走到右下角的格子。

 

题解:

对于4*3的地图:

100 101 102

-4    -4    103

-3    -3    104

-10 -10    2

 

当你走到(3,3)的时候,你的最优选择从(2,3)来

但是当你走到(4,3)的时候你会发现‘(3,3)的最优选择应该来自(3,2)

假如是直接按照dp[i][j]表示走到(i,j)格子的最优情况,然后往右边和下面格子递推,这样的递推方式是错的。

 

这个时候应该枚举不变点,就是从左上到右下的路径上总会出现一个点不需要更改高度的,枚举这样的点,然后以这个点为中心递推回左上角和右下角

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<algorithm>
#include<stack>
#define ll long long 
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 107;
ll dp[maxn][maxn], hei[maxn][maxn];
int n, m;
//逆向推到(1,1)
ll DP1(int x, int y) {
	for (int i = x; i; i--) {
		for (int j = y; j; j--) {
			if (i == x && j == y)
				continue;
			ll to = hei[x][y] - (x - i + y - j);
			if (hei[i][j] < to) {
				dp[i][j] = 1e18;
				continue;
			}
			if (i == x)
				dp[i][j] = dp[i][j + 1] + hei[i][j] - to;
			else if (j == y)
				dp[i][j] = dp[i + 1][j] + hei[i][j] - to;
			else
				dp[i][j] = min(dp[i][j + 1], dp[i + 1][j]) + hei[i][j] - to;
		}
	}
	return dp[1][1];
}
//顺推到dp[n][m]
ll DP2(int x, int y) {
	for (int i = x; i <= n; i++) {
		for (int j = y; j <= m; j++) {
			if (i == x && j == y)
				continue;
			ll to = hei[x][y] + (i - x + j - y);
			if (hei[i][j] < to) {
				dp[i][j] = 1e18;
				continue;
			}
			if (i == x)
				dp[i][j] = dp[i][j - 1] + hei[i][j] - to;
			else if (j == y)
				dp[i][j] = dp[i - 1][j] + hei[i][j] - to;
			else
				dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + hei[i][j] - to;
		}
	}
	return dp[n][m];
}
int main() {
	int t; cin >> t;
	while (t--) {
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++)
				cin >> hei[i][j];
		ll ans = 1e18;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				//枚举不变点
				dp[i][j] = 0;
				ans = min(ans, DP1(i, j) + DP2(i, j));
			}
		}
		cout << ans << endl;
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值