2021.10.17CSP模拟赛 赛后总结

这一场个人感觉考得还是很不错的,至少能拿的分都拿了。

A. [SCOI2009]生日快乐

乍一看以为是一道数学题,然后看到下面的数据范围:\(1 \leq N \leq 10\)

突然发现写个爆搜就可以了(其实还是想了很长时间 QwQ)。

我们存 3 个变量 \(dfs(x, y, k)\) 表示在 \(x * y\) 的矩形中切 \(k\) 刀,长和宽最小比例最大是多少。

我们枚举切几刀,然后上下左右都要搜一遍,求个最大值即可。

代码十分简洁。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

double n, m;
int k;

inline double dfs(double x, double y, int k){
	if(k == 1) return max(x / y, y / x);
	double res = 1e9;
	for(int i = 1; i <= (k >> 1); i++){
		double tx = x * i / k, ty = y * i / k;
		res = min(res, max(dfs(tx, y, i), dfs(x - tx, y, k - i)));
		res = min(res, max(dfs(x, ty, i), dfs(x, y - ty, k - i)));
	}
	return res;
}

int main(){
	scanf("%lf%lf%d", &n, &m, &k);
	printf("%.6lf\n", dfs(n, m, k));
	return 0;
}

B.「POI2011 R2 Day1」垃圾运输 Garbage

emm……这道题考场上看了一眼,觉得有点困难,所以就先弃了,去看 \(T3\) 了,最后也没时间写了,后来发现这道题可能比 \(T3\) 好想一点,但是不好写……

而且内部 \(oj\)\(spj\) 有锅,在洛谷上能 \(AC\) 的代码交上去只有 52 分(还好没写)。

下面进入正文:

我们发现对于当前颜色和目标颜色一样的边不需要考虑,所以我们只把需要改变颜色的边加入到图里。

然后其实就是找欧拉回路,也就是找环,然后就没别的了。

提前判断一下如果有度数为奇数的点,直接输出 NIE

(洛谷AC代码)

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e5 + 10;
const int M = 1e6 + 10;
int n, m;
struct node{
	int v, nxt;
}edge[M << 1];
int head[N], tot = 1;
int du[N], vis[M << 1];
int stk[M], top;
int cnt;

inline void add(int x, int y){
	edge[++tot] = (node){y, head[x]};
	head[x] = tot;
}

inline void dfs(int x, int root){
	stk[++top] = x;
	du[x] -= 2;
	for(int i = head[x]; i; i = edge[i].nxt){
		head[x] = i;
		if(vis[i]) continue;
		vis[i] = vis[i ^ 1] = 1;
		int y = edge[i].v;	
		if(y == root && x != root){
			cnt++;
			stk[++top] = y;
			return;
		}
		dfs(y, root);
		return;
	}
}

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i++){
		int u, v, p, q;
		scanf("%d%d%d%d", &u, &v, &p, &q);
		if(p ^ q){
			add(u, v), add(v, u);
			du[u]++, du[v]++;
		}
	}
	for(int i = 1; i <= n; i++)
		if(du[i] & 1){
			puts("NIE");
			return 0;
		}
	for(int i = 1; i <= n; i++){
		if(!du[i]) continue;
		while(du[i]) dfs(i, i);
	}
	printf("%d\n", cnt);
	for(int i = 1; i <= top; i++){
		int u = stk[i], k = 1;
		i++; 
		while(stk[i] != u && i <= top)
			k++, i++;
		printf("%d ", k);
		for(int j = i - k; j <= i; j++)
			printf("%d ", stk[j]);
		puts("");
	}
	return 0;
}

C. [JSOI2010]快递服务

一道非常巧妙的 \(dp\),考场上写了 3h,还好最后调出来了,心态没炸。

考虑朴素的状态 \(f[i][a][b][c]\),应该很容易看出来含义。

即,第 \(i\) 的个询问,三辆车分别在 \(a,b,c\) 三点时的最小油费。

但是这样转移的话时间,空间都会爆炸,所以考虑优化。

我们发现,对于上述状态,\(a,b,c\) 中必定有一个等于 \(p[i]\) 即当前询问的节点编号。

所以我们可以压掉这一维,只用剩下两维进行转移,那么:

设:\(x\) 为上一次询问的节点,\(y\) 为当前询问节点。

\(f[i][a][b] = min(f[i - 1][a]][b] + dis(x, y))\) (上一次在 \(x\) 点的车走到 \(y\)

\(f[i][a][b] = min(f[i - 1][x][b] + dis(a, y))\) (上一次在 \(a\) 点的车走到 \(y\)

\(f[i][a][b] = min(f[i - 1][a][x] + dis(b, y))\) (上一次在 \(b\) 点的车走到 \(y\)

然后本题应该就可以 \(A\) 了,因为题目给了 \(1G\) 的空间……

但其实还可以滚动一下。

我的代码强制 \(f\) 的第二维 \(<\) 第三维,其实可以不用。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

inline int read(){
	int y = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') y = (y << 3) + (y << 1) + ch - '0', ch = getchar();
	return y;
}

const int N = 210;
const int M = 1010;
int dis[N][N], f[2][N][N];//f[i][x][y] (x <= y)
int n, y, cnt, ans = 1e9;
int x, l;

int main(){
	// freopen("C.in", "r", stdin);
	// freopen("C.out", "w", stdout);
	n = read();
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			dis[i][j] = read();
	memset(f, 0x3f, sizeof(f));
	x = 1;
	f[0][2][3] = 0;
	while(scanf("%d", &y) != EOF){
		l ^= 1;
		memset(f[l], 0x3f, sizeof(f[l]));
		cnt++;
		// cout << cnt << " " << y << endl;
		for(int i = 1; i <= n; i++)
			for(int j = i; j <= n; j++){
				f[l][i][j] = min(f[l][i][j], f[l ^ 1][i][j] + dis[x][y]);
				if(x < j) f[l][x][j] = min(f[l][x][j], f[l ^ 1][i][j] + dis[i][y]);
				else f[l][j][x] = min(f[l][j][x], f[l ^ 1][i][j] + dis[i][y]);
				if(x < i) f[l][x][i] = min(f[l][x][i], f[l ^ 1][i][j] + dis[j][y]);
				else f[l][i][x] = min(f[l][i][x], f[l ^ 1][i][j] + dis[j][y]);
			}
		x = y;
	}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			ans = min(ans, f[l][i][j]);
	printf("%d\n", ans);
	return 0;
}

D. [HNOI2010]物品调度

emm……还不会。

考场上输出 \(n - 1\) 得了 10pts。

posted @ 2021-10-18 22:16  xixike  阅读(56)  评论(0编辑  收藏  举报