Atcoder Beginner Contest 271 A~E 题题解

前言

一场掉分的 ABC /kk

在花了 10 10 10 分钟看完前 4 4 4 题后,选择了赛场上大部分人使用的:先开 D,再按顺序做 A、B、C。(打题的时候才发现应该先开 C 的 QwQ)

但却因为 CSP-S 神秘的太空射线使得自身膨胀,结果 E 想复杂 ×   2 \times \ 2 × 2,当场去世。

都是因为 CSP-S,不是我的错

ABC 271 A 题

题目大意:

你需要把 x x x 转换成一个两位的 16 16 16 进制数,若不足两位则加上前导 0 0 0

解法分析:

此题考查人们对输出语句的熟悉程度。

题目解法不多说,直接上代码。

AC Code:

# include 
using namespace std;

# define ll long long
# define lf double
# define GO(i,a,b) for(ll i = a; i <= b; i ++)
# define RO(i,b,a) for(ll i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a, b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

int n;

int main(){
	scanf("%d", &n);
	printf("%02llX", n);
	return 0;
}

ABC 271 B 题

题目大意:

给你 n n n 个数组,第 i i i 个数组大小为 k i k_i ki,再有 q q q 次询问,每次问你第 x x x 个数组的第 y y y 项是什么。

解法分析:

说句闲话,vector 是个好东西。

这道题显然需要动态大小的数组,那就非 vector 莫属了。

AC Code:

# include 
using namespace std;

# define ll long long
# define lf double
# define GO(i,a,b) for(ll i = a; i <= b; i ++)
# define RO(i,b,a) for(ll i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a, b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 2e5 + 7;

int n, q;
int l;
vector <int> a[maxn];
int x, y;

int main(){
	cin >> n >> q;
	GO (i, 1, n){
		scanf("%d", &l);
		GO (j, 1, l){
			int p;
			scanf("%d", &p);
			a[i].push_back(p);
		}
	}
	while (q --){
		scanf("%d %d", &x, &y);
		printf("%d\n", a[x][y - 1]);
	}
	return 0;
}

ABC 271 C 题

题目大意:

高桥有 n n n 本书,第 i i i 本编号为 a i a_i ai。他可以不停的卖 2 2 2 本书获得一本任意编号的书,但当剩下的书不到 2 2 2 本时就不能这样了。现在高桥想要从编号为 1 1 1 的书开始读,看完一本就看下一个编号的书,如果没有就不能看了。高桥想尽量多的看书,请计算高桥最多可以看多少本书。

解法分析:

有两种做法,这里只讲二分。

这道题说到了最多,就想到了二分。考虑当现在二分到了 m i d mid mid,那么:

  • 重复编号的书可以直接卖掉
  • 剩下不重复编号但是编号大于 m i d mid mid 的书也可以卖掉

当你将所有可以买的书卖掉后,看能不能达到期望的数量即可。

AC Code:

# include 
using namespace std;

# define ll long long
# define lf double
# define GO(i,a,b) for(ll i = a; i <= b; i ++)
# define RO(i,b,a) for(ll i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a, b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 3e5 + 7;

int n, m;
int a[maxn];

bool check(int mid){
	int res = 0;
	GO (i, 1, m)
		res += (a[i] <= mid); 
	return (mid - res) * 2 <= (n - res);
}

int main(){
	cin >> n;
	GO (i, 1, n)
		scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	m = 1;
	GO (i, 2, n)
		if (a[i] != a[i - 1])
			a[++ m] = a[i];
	int l = 0, r = 1e9;
	int ans = 0;
	while (l <= r){
		int mid = (l + r) >> 1;
		if (check(mid)){
			ans = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	cout << ans;
	return 0;
}

ABC 271 D 题

题目大意:

一共有 n n n 张牌,每一面都写着一个整数。卡 i i i ( 1 ≤ i ≤ n 1≤i≤n 1in)前面写着整数 a i a_i ai,后面写着整数 b i b_i bi

你可以选择是否放置每张卡片的正面或背面可见。

确定是否可以调整卡片的正反面,使得可见整数的和恰好等于 s s s ,如果可能的话,找到卡片的位置来实现这一点。

解法分析:

这是一道一眼盯真的题。这道题目长得就像个 DP,那就设 f i , j f_{i,j} fi,j前i个数和是否能凑成j,转移方程就是 f i , j = f i − 1 , j − a i ∣ f i − 1 , j − b i f_{i,j}=f_{i-1,j-a_i} | f_{i - 1, j - b_i} fi,j=fi1,jaifi1,jbi

又说这道题需要输出路径,这就是一个经典的路径还原了。设 w i , j w_{i,j} wi,j 为计算 f i , j f_{i,j} fi,j 时做出的选择,然后从后往前递归输出即可。

AC Code:

# include 
using namespace std;

# define ll long long
# define lf double
# define GO(i,a,b) for(ll i = a; i <= b; i ++)
# define RO(i,b,a) for(ll i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a, b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 107;

int n, s;
int a[maxn], b[maxn];
bool f[maxn][10007];
char w[maxn][10007];

void out(int n, int s){
	if (n == 0)
		return ;
	out(n - 1, s - (w[n][s] == 'H' ? a[n] : b[n]));
	printf("%c", w[n][s]);
}

int main(){
	cin >> n >> s;
	GO (i, 1, n)
		scanf("%d %d", &a[i], &b[i]);
	f[0][0] = true;
	GO (i, 1, n){
		GO (j, 0, s){
			f[i][j] = max <bool> ((j >= a[i] ? f[i - 1][j - a[i]] : 0), (j >= b[i] ? f[i - 1][j - b[i]] : 0));
			if (j >= a[i] && f[i][j] == f[i - 1][j - a[i]])
				w[i][j] = 'H';
			else if (j >= b[i] && f[i][j] == f[i - 1][j - b[i]])
				w[i][j] = 'T';
			else 
                w[i][j] = 'I';
		}
	}
	printf(f[n][s] ? "Yes\n" : "No\n");
	if (f[n][s]) out(n, s);
	return 0;
}

ABC 271 E 题

题目大意:

有一个有向图, 有 N N N 个编号为 1 , … , N 1,\dots,N 1,,N 的点和 M M M 条编号为 1 , … , M 1,\dots,M 1,,M 的路。

i i i 条路从 A i A_i Ai B i B_i Bi,长度是 C i C_i Ci

给定长度为 K K K 的序列 E E E ,由 1 1 1 m m m 之间的整数组成。从城镇 1 1 1 到城镇 N N N 的道路被称为好路径,如果:

按照路径中使用的顺序排列的道路编号序列是 E E E 的子序列。

找出一条好的路径所使用的道路长度的最小和。

解法分析:

赛场上想复杂了很多次,一直以为要用 Dijsktra 来做,但其实不然。题目中说一定要按照顺序来走(构成子序列),从另一方面来看,这是在帮我们建图。因为我们只能用 E E E 中的边,还要按顺序使用,则不会出现编号靠后的边比编号靠前的边先走的情况,这就满足了 DP 的无后效性。而 DP 的另外两个性质也是显而易见的满足,于是这道题思路就出来了:设 f i f_i fi 为走到 i i i 的最短路,则按顺序遍历 E E E 的每一条边,每次更新就是 f v = min ⁡ ( f v , f u + w ) f_v = \min(f_v, f_u + w) fv=min(fv,fu+w),最后输出 f n f_n fn 即可。

AC Code:

# include 
using namespace std;

# define ll long long
# define lf double
# define GO(i,a,b) for(ll i = a; i <= b; i ++)
# define RO(i,b,a) for(ll i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a, b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 2e5 + 7;

int n, m, k;
int u[maxn], v[maxn], w[maxn];
int a, b;
ll dis[maxn];

int main(){
	cin >> n >> m >> k;
	GO (i, 1, n)
		dis[i] = 2e18;
	GO (i, 1, m)
		scanf("%d %d %d", &u[i], &v[i], &w[i]);
	dis[1] = 0;
	GO (i, 1, k){
		scanf("%d", &a);
		dis[v[a]] = min <ll> (dis[v[a]], dis[u[a]] + w[a]);
	}
	cout << (dis[n] == 2e18 ? -1 : dis[n]);
	return 0;
}

你可能感兴趣的:(Atcoder,算法,图论,c++)