SDNU_ACM_ICPC_2022_Winter_Practice_1th [个人赛]

A

Multiple of 2019 - AtCoder abc164_d - Virtual Judge

比赛时第一眼认为是01背包题,然后直接背上板子,dp我还是太辣鸡,什么动态转移,动态方程都不会,要么能分析出咋变化的,要么变变初始状态凑凑数,要么直接放弃。

思路:通常的01背包,跑一遍对于这个题目来说就是从1到1.....n中所有的方案数,因此其中并没有包括2到2....n ......n到n的方案数,因此我考虑的是每一次都把自己当作另一个背包,多路并进,即每次dp[0]都+1,具体看下面的拙图QAQ

 AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
long long a[3030];
long long b[3030];
void solve() {
	int n, s;
	cin >> n >> s;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	long long ans = 0;
	b[0] = 1;
	for (int i = 1; i <= n; i++) {
		
		for (int j = s; j - a[i] >= 0; j--) {
			b[j] = (b[j] + b[j - a[i]]) % mod;
		}
		
		ans = (ans + b[s]) % mod;
		b[0]++;
	}
	cout << ans % mod << endl;
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

B

Dividing Chocolate - AtCoder abc159_e - Virtual Judge

由题目数据可以看出,对于每一行来说我们可以直接暴力采用二进制枚举所有可能切割的情况,这里为什么会想到二进制呢,因为可以用二进制来表示每一行到底切还是不切,比如有四行,那么就用两位就可以表示所有情况:00,01,10,11四种切法,输入的时候顺便记录一下每一行的前缀和,然后再判断每一列的情况,其中切行的时候最多切h-1下

AC代码:

1.

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
int h, w, k, pre[20][1010], flag, ans = INF;
char ch;
void solve() {
	scanf("%d %d %d", &h, &w, &k);
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j <= w; j++) {
			scanf(" %c", &ch);
			pre[i][j] = pre[i][j - 1] + ch - '0';
		}
	}
	for (int state = 0; state < (1 << h - 1); state++) {
		int last = 0, sum = 0, cnt = 0, flag = 1;
		for (int j = 1; j <= w; j++) {
			for (int i = 1; i <= h; i++) {
				sum = sum + pre[i][j] - pre[i][last];
				if (sum > k) {
					if (j - last == 1) {
						flag = 0;
						break;
					}
					i = 0, sum = 0, last = j - 1;
					cnt++;
				}
				if (i == h || state & (1 << (i - 1))) {
					sum = 0;
				}
			}
			if (!flag) {
				break;
			}
		}
		if (flag) {
			int m = 0;
			for (int l = 0; l < h - 1; l++) {
				if ((state >> l) & 1) {
					m++;
				}
			}
			ans = min(ans, m + cnt);
		}
	}
	printf("%d\n", ans);
	return;
}
int main() {
	//ios::sync_with_stdio(0);
	//cin.tie(0);
	solve();
	return 0;
}
/*

*/

2.(个人感觉第二个更容易理解一点)

对于每一行来说我们可以直接暴力采用二进制枚举所有可能切割的情况,这里为什么会想到二进制呢,因为可以用二进制来表示每一行到底切还是不切,比如有四行,那么就用两位就可以表示所有情况:00,01,10,11四种切法,所以就可以通过位运算&1来判断当前这种情况下这一行切没切,从而实现判断这一列符合条件的时候也判断了行有没有切到位,如果切到位了,这一列肯定不会超,如果没切到位,那肯定就是行没有切到位,这时候再去切某一列就没有任何意义了,如果一行里超了的话那只能竖着切一刀,然后再判断一下如果竖着切了一刀后满不满足题意,如果还不行那就是行切少了,直接再判断下一种切行的情况而不更新答案

#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const double eps = 1e-8;
const int NINF = 0xc0c0c0c0;
const int INF  = 0x3f3f3f3f;
const ll  mod  = 1e9 + 7;
const ll  N = 1e3 + 5;

int n,m,k,z;
string s[15];
int c[15];

bool f(int j){//返回true代表要切一刀竖着的,因为至少有一块已经超过k了
	int id=0;
	for(int i=0;i<n;i++){
		c[id]+=s[i][j]=='1';
		if(c[id]>k) return true;//超过k了 要切哦
		if(z>>i & 1) id++;
	}
	return false;//不用切 依旧满足
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>k;
	for(int i=0;i<n;i++) cin>>s[i];
	int ans=0x3f3f3f3f;
	for(z=0;z<1<<n-1;z++){//长度为n那么可以切n-1刀,枚举0~2^(n-1)-1的状态,1代表切,0代表不切。
		memset(c,0,sizeof(c));
		bool ok=false;
		int cnt=__builtin_popcount(z);//横着切了多少刀
		for(int j=0;j<m;j++){//现在横着切完要贪心求竖着了
			if(f(j)){//要竖着切一刀
				cnt++;//竖着切了一刀
				memset(c,0,sizeof(c));//那么切完之后竖着的每一块又都是0了
				ok=f(j);//如果竖着切了一刀,哪怕是一列还是不行,那说明横着切少了,无论如何都不满足
			}
		}
		if(!ok) ans=min(ans,cnt);//满足条件的时候记录最小值
	}
	cout<<ans;
	return 0;
}

C

Maximum Volume - AtCoder abc159_c - Virtual Judge

C题打眼一看求最大体积,然后长宽高的和等于L,根据不等式就能知道当且仅当a=b=c的时候a*b*c是最大的

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
void solve() {
	double l;
	cin >> l;
	double s = l / 3.0;
	cout << fixed << setprecision(11) <<s * s * s << endl;
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

D

String Palindrome - AtCoder abc159_b - Virtual Judge

判断回文,要判断三个条件就可以了,N是输入字符串总长度,第一个条件是给的字符串就得是回文的,第二个条件是从第一个字符到第(N-1)/2个字符之间的字符串是回文的,第三个条件是从第(N+3)/2个字符到第N个字符之间的字符串是回文的,然后就是输出了

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
void solve() {
	string s;
	cin >> s;
	int len = s.length();
	int x = (len - 1) / 2, y = (len + 3) / 2;//x=3,y=5,len=7
	bool ok = true;
	for (int i = 0; i < len; i++) {
		if (s[i] != s[len - i - 1]) {
			ok = false;
			break;
		}
	}
	for (int i = 0; i < x; i++) {
		if (s[i] != s[x - i - 1]) {
			ok = false;
			break;
		}
	}
	for (int i = len - 1; i >= y; i--) {
		if (s[i] != s[y + len - i - 2]) {
			ok = false;
			break;
		}
	}
	if (ok) {
		cout << "Yes" << endl;
	}
	else {
		cout << "No" << endl;
	}
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

E

Banned K - AtCoder abc159_d - Virtual Judge

这个题就是组合题,给一个序列,然后用map记录一下每个数的出现的次数,求这些数一共能形成多少对两个数相等的组合,也就是一个数必须出现了两次以上才能形成至少一种组合,否则形不成,我先假设没把第k个数拿出去,就是x*(x-1)/2,算完了以后,我再算把第k个数拿出去的情况,这样我只需要把算的总和减去x*(x-1)/2然后再加上(x-1)*(x-2)/2,就是答案

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
long long n;
long long a[200010];
map<long long, long long> b;
void solve() {
	cin >> n;
	for (ll i = 1; i <= n; i++) {
		cin >> a[i];
		b[a[i]]++;
	}
	ll ans = 0;
	map<ll, ll>::iterator it;
	for (it = b.begin(); it != b.end(); it++) {
		ll x = it->second;
		if (x >= 2) {
			ans += x * (x - 1) / 2;
		}
	}
	for (ll i = 1; i <= n; i++) {
		if (b[a[i]] >= 2) {
			
			cout << ans - b[a[i]] * (b[a[i]] - 1) / 2 + (b[a[i]] - 1) * (b[a[i]] - 2) / 2 << endl;
		}
		else {
			cout << ans << endl;
		}
	}
	return;
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

F

Multiple of 2019 - AtCoder abc164_d - Virtual Judge

这就是大数取模,只要同一个模数大于1,说明一定至少存在两个数,假设x,y,x%2019=y%2019,x = a*2019+b,y = c*2019+b,直接假设数字表示一下,x=20192020,y=2020->x=2019*10001+1,y=2019*1+1,因此能够得出(x-y)的绝对值一定是2019的倍数,因此说明只要一个模数能取出来的次数大于1,那么至少能得到一个被2019整除的数

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
long long f[2022];
char s[200010];
void solve() {
	scanf("%s", s + 1);
	long long len = strlen(s + 1);
	f[0] = 1;
	long long sign = 0;
	long long a = 1;
	long long ans = 0;
	for (long long i = len; i >= 1; i--) {
		sign = (sign + (s[i] - '0') * a) % 2019;
		a = a * 10 % 2019;
		ans += f[sign];
		f[sign]++;
	}
	cout << ans << endl;
	return;
}
int main() {
	//ios::sync_with_stdio(0);
	//cin.tie(0);
	//
	solve();
	return 0;
}

G

A Simple Problem with Integers - POJ 3468 - Virtual Judge

线段树or树状数组模板题,菜菜的我只能算是会树状数组,线段树emmm

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
long long tree1[100010],tree2[100010];
int a[100010];
int n,q;
int lowbit(int x) {
	return x & -x;
}
void add(int p,int x) {
	long long v = p * x;
	for (int i = p; i <= n; i += lowbit(i)) {
		tree1[i] += x;
		tree2[i] += v;
	}
	return;
}
long long get(int x) {
	long long ans = 0;
	for (int i = x; i > 0; i -= lowbit(i)) {
		ans += (x + 1) * tree1[i] - tree2[i];
	}
	return ans;
}
void solve() {
	cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	vector<long long> ve(n + 10);
	adjacent_difference(a + 1, a + 1 + n, ve.begin() + 1);
	for (int i = 1; i <= n; i++) {
		tree1[i] += ve[i];
		tree2[i] += 1ll * ve[i] * i;
		long long j = i + lowbit(i);
		if (j <= n) {
			tree1[j] += tree1[i];
			tree2[j] += tree2[i];
		}
	}
	while (q--) {
		char op;
		cin >> op;
		if (op == 'C') {
			int x, y, z;
			cin >> x >> y >> z;
			add(x, z);
			add(y + 1, -z);
		}
		else {
			int x, y;
			cin >> x >> y;
			cout << get(y) - get(x - 1) << endl;
		}
	}
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

H

Networking - POJ 1287 - Virtual Judge

最小生成树模板题,遇到最小生成树,率先想到Kruscal,碰到稠密图的prim那就emmmm

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
int n, e;
struct node {
	int from, to, cost;
}poi[10000];
int fa[110];
bool cmp(node a, node b) {
	return a.cost < b.cost;
}
int get(int x) {
	return x == fa[x] ? x : fa[x] = get(fa[x]);
}
void solve() {
	long long ans = 0;
	for (int i = 1; i <= e; i++) {
		cin >> poi[i].from >> poi[i].to >> poi[i].cost;
	}
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
	}
	sort(poi + 1, poi + 1 + e, cmp);
	for (int i = 1; i <= e; i++) {
		int x = get(poi[i].from);
		int y = get(poi[i].to);
		if (x != y) {
			fa[x] = y;
			ans += poi[i].cost;
		}
	}
	cout << ans << endl;
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	while (cin >> n) {
		if (n == 0) {
			break;
		}
		cin >> e;
		solve();
	}
	return 0;
}

J

Base K - AtCoder abc220_b - Virtual Judge

N进制转十进制然后再相乘

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
void solve() {
	long long a, b, k;
	cin >> k >> a >> b;
	long long suma = 0, sumb = 0;
	long long x = 1, y = 1;
	while (a) {
		suma += (a % 10) * x;
		x *= k;
		a /= 10;
	}
	while (b) {
		sumb += (b % 10) * y;
		y *= k;
		b /= 10;
	}
	cout << suma * sumb << endl;
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

K

Long Sequence - AtCoder abc220_c - Virtual Judge

求给定数列循环多少下的和数超过x,判断+跑循环就好了

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
int a[100010];

void solve() {
	long long n, x;
	long long sum = 0;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		sum += a[i];
	}
	cin >> x;
	bool ok = true;
	long long ans;
	while (ok) {
		ans = x / sum * n;
		x %= sum;
		
		for (int i = 1; i <= n; i++) {
			x -= a[i];
			if (x < 0) {
				ok = false;
				ans += i;
				break;
			}
		}
	}
	cout << ans << endl;
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

L

迷宫问题 - POJ 3984 - Virtual Judge

记忆化搜索bfs,单纯的bfs这个题简单,难在他还要你还原怎么走的,该题的起点就是左上角,终点就是右下角,我们只需要在原图的基础上再另外建立一张图来记录当前这个点是从哪个点走过来的,然后跑一遍bfs,但这样还只能倒着输出整条路径,所以可以用栈先进后出的特性,保存一遍路径,再挨个弹栈输出就行了

AC代码:

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <numeric>
#include <iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-6;
int dir[4][2] = { {1,0},{0,1},{-1,0},{0,-1} };
int mp[10][10];
struct node {
	int x, y;
}poi[10][10];
int cnt;
bool vis[10][10];
void solve() {
	for (int i = 1; i <= 5; i++) {
		for (int j = 1; j <= 5; j++) {
			cin >> mp[i][j];
		}
	}
	cnt = 0;
	vis[1][1] = true;
	queue<node> q;
	q.push(node{ 1,1 });
	while (!q.empty()) {
		struct node now = q.front();
		q.pop();
		if (now.x == 5 && now.y == 5) {
			break;
		}
		for (int i = 0; i < 4; i++) {
			node next;
			next.x = now.x + dir[i][0];
			next.y = now.y + dir[i][1];
			if (!vis[next.x][next.y] && next.x > 0 && next.x <= 5 && next.y > 0 && next.y <= 5 && mp[next.x][next.y] != 1) {
				q.push(next);
				vis[next.x][next.y] = true;
				poi[next.x][next.y] = now;
			}
		}
	}
	stack<node> st;
	struct node pre = { 5, 5 };
	while (1) {
		st.push(pre);
		if (pre.x == 1 && pre.y == 1) {
			break;
		}
		pre = poi[pre.x][pre.y];
	}
	while (!st.empty()) {
		
		cout << "(" << (st.top().x -= 1) << ", " << (st.top().y -= 1) << ")" << endl;
		st.pop();
	}
	return;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	solve();
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值