AtCoder Beginner Contest 229 (A,B,C,D,E,F)

A. First Grid

签到题。
判断对角线是否同时为 ‘.’ 即可
ACcode

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;

int main(){
	
	char a, b, c, d;
	cin >> a >> b >> c >> d;
	if(b == '.' && c == '.') cout << "No" << endl;
	else if(a == '.' && d == '.') cout << "No" << endl;
	else cout << "Yes" << endl;
	
	return 0;
}

B. Hard Calculation

模拟题。
从低位到高位依次判断是否进位即可。
ACcode

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;

int main(){

	ll a, b;
	cin >> a >> b;
	
	int res = 1;
	while(a && b){
		int x = a % 10;
		int y = b % 10;
		if(x + y > 9) res = 0;
		a /= 10;
		b /= 10;
	}	
	
	if(res) cout << "Easy" << endl;
	else cout << "Hard" << endl;
	
	return 0;
}

C. Cheese

贪心。
按照美味度排序,优先取美味度高的奶酪即可。
ACcode

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;

vector<pair<ll, ll> > v; // 可以写结构体排序

int main(){

	ll n, w;
	cin >> n >> w;
	ll x, y;
	for(int i = 1; i <= n; i++){
		cin >> x >> y;
		v.push_back({x, y});
	}
	
	sort(v.begin(), v.end());
	
	ll res = 0;
	for(int i = n-1; i >= 0; i--){
		ll tmp = min(w, v[i].second);
		res += tmp * v[i].first;
		w -= tmp; 
	}
	
	cout << res << endl;
	
	return 0;
}

D. Longest X

尺取法。
左端点遇到 ‘.’ 时,如果区间内已经有 k 个 ‘.’ 就释放右端点,注意处理 k = 0。
ACcode

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;

int main(){

	string s;
	cin >> s;
	int k;
	cin >> k;
	
	int len = s.size(), res = 0;
	int l = 0, r = -1, cnt = 0;
	while(r+1 < len){
		r++;
		if(s[r] == '.'){
			if(cnt < k) cnt++;
			else{
				while(l <= r && s[l] == 'X') l++; // 碰到一个 . 结束 
				l++; // 移过去这个 . 
			}
		}
		res = max(res, r-l+1); 
	}
	
	cout << res << endl;
	
	return 0;
}

E. Graph Destruction

并查集,思维题。
逆序遍历所有端点跑并查集即可。
ACcode

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;

const int maxn = 2e5 + 5;
vector<int> ma[maxn];
int f[maxn], ans[maxn];
int cnt = 0;

int find(int x){
	if(f[x] == x) return x;
	return f[x] = find(f[x]);
}

void join(int x, int y){
	int fx = find(x);
	int fy = find(y);
	if(fx != fy) cnt--, f[fx] = fy; // 联通块合并了
}

int main(){

	int n, m;
	scanf("%d %d", &n, &m);
	int u, v;
	for(int i = 1; i <= m; i++){
		scanf("%d %d", &u, &v);
		ma[u].push_back(v);
		ma[v].push_back(u);
	}
	
	for(int i = 1; i <= n; i++) f[i] = i;
	
	for(int i = n; i >= 1; i--){ // 逆序遍历
		ans[i] = cnt;
		cnt++; // 增加 i 号点,增一个联通块
		for(auto y : ma[i]) if(y > i) join(i, y); // 仅使用大于 i 的边,等价于抹去了 1 到 i 
	}
	
	for(int i = 1; i <= n; i++) cout << ans[i] << endl;
	
	return 0;
}

F. Make Bipartite

DP。
考虑断开 1 1 1 n n n,把环变成链。
默认 0 0 0 号点在左集。
定义:

  • d p i , 0 , 0 dp_{i,0,0} dpi,0,0 表示把 i i i 号点放到右集 1 1 1 号点在右集时要删除的边的权值和
  • d p i , 0 , 1 dp_{i,0,1} dpi,0,1 表示把 i i i 号点放到右集 1 1 1 号点在左集时要删除的边的权值和
  • d p i , 1 , 0 dp_{i,1,0} dpi,1,0 表示把 i i i 号点放到左集 1 1 1 号点在右集时要删除的边的权值和
  • d p i , 1 , 1 dp_{i,1,1} dpi,1,1 表示把 i i i 号点放到左集 1 1 1 号点在左集时要删除的边的权值和

注意初始化:
默认 0 0 0 号点在左集, 1 1 1 号点在左集时需要删除 a 1 a_1 a1,

  • d p 1 , 0 , 0 = 0 , d p 1 , 1 , 1 = a 1 dp_{1,0,0} = 0, dp_{1,1,1} = a_1 dp1,0,0=0,dp1,1,1=a1

1 号点不能同时出现在两个集合,这两种情况不存在,初始为无限大

  • d p 1 , 0 , 1 = d p 1 , 1 , 0 = i n f dp_{1,0,1} = dp_{1,1,0} = inf dp1,0,1=dp1,1,0=inf

状态转义方程参见代码,注意最后把链变为环。

ACcode

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;

const int maxn = 2e5 + 5;
ll a[maxn], b[maxn];
ll dp[maxn][2][2];

int main(){

	int n;
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 1; i <= n; i++) cin >> b[i];

	dp[1][0][0] = 0; dp[1][1][1] = a[1];
	dp[1][0][1] = dp[1][1][0] = 1e18;
	for(int i = 2; i <= n; i++){
		// 和前一个点在同一个集合需要删除相连的边,不在同一个集合时不需要删除
		dp[i][0][0] = min(dp[i-1][0][0] + b[i-1], dp[i-1][0][1]); 
		dp[i][1][0] = min(dp[i-1][1][0] + b[i-1], dp[i-1][1][1]);
		// 在左集时,需要多删除和 0 号点相连的边
		dp[i][0][1] = min(dp[i-1][0][0], dp[i-1][0][1] + b[i-1]) + a[i]; 
		dp[i][1][1] = min(dp[i-1][1][0], dp[i-1][1][1] + b[i-1]) + a[i];
	}
	
	ll res = 1e18;
	for(int i = 0; i < 2; i++){
		for(int j = 0; j < 2; j++){
			ll tmp = dp[n][i][j] + (i == j ? b[n] : 0); // 把链变为环
			res = min(res, tmp);
		}
	}	
	cout << res << endl;	
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值