Codeforces Round 933 (Div. 3) (A~E)

Codeforces Round 933 (Div. 3) (A~E)

目录:A B C D E

A题:Rudolf and the Ticket

标签: 暴力枚举(brute force)数学(math)排序算法(sortings)双指针算法(two pointers)

题目大意

  • 给一个长度为n的数组b,和一个长度为m的数组c,还有一个整数k。
  • 从b,c中各选一个数,问有多少种选法使这两个数小于等于k

思路

  • 前缀和快速得出在b数组中小于等于k - c 的数的个数

AC代码

#include 
using namespace std;
const int N = 2020;
int cnt[N];
void solve()
{
	int n, m, k;
	cin >> n >> m >> k;
	memset(cnt, 0, sizeof cnt);
	for(int i = 0; i < n; i++)
	{
		int a; cin >> a;
		cnt[a] ++;
	}
	for(int i = 1; i <= 2010; i ++)
		cnt[i] += cnt[i-1];
	int res = 0;
	for(int i = 0; i < m; i++)
	{
		int b; cin >> b;
		if(k>=b)res += cnt[k - b];
	}
	cout << res << endl;
} 
int main()
{
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}

B题:Rudolf and 121

标签: 贪心策略(greedy)数学(math)

题目大意

  • 有一个由 n个整数组成的数组 a ,元素的编号从 1 到 n 。在一次操作中,他可以选择索引 i ( 2 ≤ i ≤ n) 并赋值:
  • ai−1 = ai−1 − 1
  • ai = ai − 2
  • ai+1 = ai+1 − 1
  • 多次使用这个运算。问是否能将a 中元素全变为0

思路

  • 如果要将第一个数变为0,那么只能对第二个数进行操作,变换完后将不会在对第二个数进行操作。
  • 那么同理:将第二个数变为0,只能对第三个进行操作…

AC代码

#include 
using namespace std;
const int N = 1000100;
int cnt[N];
void solve()
{
	int n; cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> cnt[i];
	for(int i = 1; i <= n - 2; i++)
	{
		if(cnt[i] > 0)
		{
			cnt[i+1] -= cnt[i] * 2;
			cnt[i+2] -= cnt[i];
			cnt[i] = 0;
		}	 
	}
	for(int i = 1; i <= n; i++)
	{
		if(cnt[i] != 0) {
			cout << "NO" << endl;
			return ;
		}
	}
	cout << "YES" << endl;
} 
int main()
{
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}

C题:Rudolf and the Ugly String

标签: 动态规划(dp)字符串处理(strings)

题目大意

  • 给定字符串s,问:最少操作几次使 s 不包含子串"map"和"pie"。
  • 如果字符串 b 中存在一个连续的字符段等于 a ,则字符串 a是 b 的子串。

思路

  • 如果s中含有 “map” ,删除 ‘a’,含有"pie",删除 ‘i’ 即可消掉
  • 特判如果含有 “mapie” 这个子串,删除 ‘p’ 即可将map和pie都消掉(只需要删一次)

AC代码

#include 
using namespace std;
void solve()
{
	int n; string s;
	cin >> n >> s;
	int t = s.size(), res = 0;
	for(int i = 0; i < t - 2; i++)
	{
		string subS = s.substr(i, 3);
		if(subS == "map" || subS == "pie")
			res ++;
		if(i <= t - 5 && s.substr(i, 5) == "mapie")
			res--;
	}
	cout << res << endl;
} 
int main()
{
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}

D题:Rudolf and the Ball Game

标签: 动态规划(dp)实现问题,编程技巧,模拟(implementation)

题目大意

  • n 个人编号1~n站成一个圈,有一个球在编号为x的人手上,进行m次传球
  • 给定每次投掷的距离和每次投掷的方向(顺时针或逆时针),也有可能不知道方向。
  • 计算出 m 次抛球后可能拿到球的人。

思路

  • 为了方便将编号1~n 进行减一
  • 状态表示:dp[i][j]表示 进行i次传球,球可不可能在编号为j的人手上 (值:1 0)
  • 初始值:没进行传球时球在 x,即:dp[0][x] = 1;
  • 转态转移:
    • 逆时针传球:dp[i][j] |= dp[i-1][(j + f[i].fr) % n];
    • 顺时针传球:dp[i][j] |= dp[i-1][((j - f[i].fr) % n + n) % n];

AC代码

#include 
#define fr first
#define se second
using namespace std;
typedef pair<int, char> PII;
const int N = 1010;
PII f[N];
int dp[N][N];
void solve()
{
	int n, m, x;
	cin >> n >> m >> x;
	x--;
	for(int i = 0; i <= m; i++)
		for(int j = 0; j <= n; j++)
			dp[i][j] = 0;
	for(int i = 1; i <= m; i++)
		cin >> f[i].fr >> f[i].se;
	dp[0][x] = 1;
	int res = 0;
	for(int i = 1; i <= m; i++)
		for(int j = 0; j < n; j++)
		{
			if(f[i].se == '?' || f[i].se == '1')
				dp[i][j] |= dp[i-1][(j + f[i].fr) % n];
            if(f[i].se == '?' || f[i].se == '0')
				dp[i][j] |= dp[i-1][((j - f[i].fr) % n + n) % n];
			if(i==m && dp[i][j]==1) res ++; 
		}
	cout<<  res << endl;
	for(int i = 0; i < n; i++)
		if(dp[m][i] == 1) cout << i + 1 << " ";
	cout << "\n";
}  
int main()
{
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}

E题:Rudolf and k Bridges

标签: 数据结构(data structures)动态规划(dp)

题目大意

  • n行和 m列组成的网格。第 i行和第 j 列的交点包含数字 ai,j —相应单元格的深度。第一列和最后列中的所有单元格都与河岸相对应,因此它们的深度为 0。
  • 可以选择每一行安装支架,建造一座桥。
    1. 起点和终点必须含有支架
    1. 支架间间隔最多为d
  • 在(i , j)建支架成本为 ai,j + 1
  • 问:建造连在一起的k座桥,所需最少成本。

思路

  • 直接给出定义,dp[i] 为在 i 处建造支柱最小的花费。dp[i-d-1]~dp[i-1] 里面找最小值转移到dp[i]
  • 下标 i-dis-1 的位置有支柱,最远可以让下标 i 位置造一个支柱,即: dp[i]=min{ dp[i-d-1] ~ dp[i-1] } + d[i] + 1
  • 连续 k 座桥用前缀和或滑动窗口即可
  • dp这样的时间复杂度为 n * m * d 会T掉
  • 每次存储dp[i-d-1] ~ dp[i-1]最小值转移,即用单调队列优化详情看AC代码,时间复杂度 n * m

基础dp代码 (会T掉)

#include
#define LL long long
using namespace std;
const int N = 200005;
LL 	d[N], ans[111];
void solve()
{
	int n, m, k, dis;
	cin >> n >> m >> k >> dis;
	dis++;
	for(int j = 1;j <= n; j++){
		for(int i = 1;i <= m; i++){
			cin >> d[i];
			LL dd = 1e18;
			for(int k = i - 1; k >= max(1, i - dis); k--)
				dd = min(dd, d[k]);
			if(i == 1) dd = 0;
			d[i] += dd + 1;
		}
		ans[j] = d[m];
	}
	for(int i = 1;i <= n; i++)
		ans[i] += ans[i-1];
	LL mi = 1e18;
	for(int i = k;i <= n; i++)
		mi = min(mi, ans[i] - ans[i-k]);
	cout << mi << '\n';
}
int main(){
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}

AC代码

#include
#define LL long long
using namespace std;
const int N = 200005;
LL d[N], ans[111], q[N];
void solve()
{
	int n, m, k, dis;
	cin >> n >> m >> k >> dis;
	dis++;
	for(int j = 1;j <= n; j++){
		int hh = 0, tt = -1; //hh队列头,tt队列尾
		q[0] = 0; 
		for(int i = 1;i <= m; i++){
			cin >> d[i]; 
			if(q[hh] < i - dis) hh++; // 队列头下标超出距离,删除队头
			d[i] += d[q[hh]] + 1;	  // q[hh]即为dp[i-d-1] ~ dp[i-1]最小值下标
            // 队列不为空,不断删除队尾比d[i]要小的值
			while(tt >= hh && d[i] <= d[q[tt]]) 
				tt--;	 //删除队尾
			q[++tt] = i; //加入队列
		}
		ans[j] = d[m]; // d[m]表示在m处建造支柱的最小值即为答案
	}
	for(int i = 1;i <= n; i++)
		ans[i] += ans[i-1];
	LL mi = 1e18;
	for(int i = k;i <= n; i++)
		mi = min(mi, ans[i] - ans[i-k]);
	cout << mi << '\n';
}
int main(){
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}

logo

//へ     /|
//  /\7    ∠_/
//  / │   / /
// │ Z _,< /   /`ヽ
// │     ヽ   /  〉
//  Y     `  /  /
// イ● 、 ●  ⊂⊃〈  /
// ()  へ    | \〈
//  >ー 、_  ィ  │ //
//  / へ   / ノ<| \\
//  ヽ_ノ  (_/  │//
//	  7       |/
//
/*
          __   _,--="=--,_   __
         /  \."    .-.    "./  \
        /  ,/  _   : :   _  \/` \
        \  `| /o\  :_:  /o\ |\__/
         `-'| :="~` _ `~"=: |
            \`     (_)     `/
     .-"-.   \      |      /   .-"-.
.---{     }--|  /,.-'-.,\  |--{     }---.
 )  (_)_)_)  \_/`~-===-~`\_/  (_(_(_)  (
(                         				)
 )                                     (
'---------------------------------------'
*/

你可能感兴趣的:(算法竞赛,算法,codeforces,c++)