云斗杯 · 五月 Silver 组模拟赛T1T2题解

为啥只有T1和T2? 因为T3T4不会

T1:无所谓的第一道题

题意:给定一张 \(n \times n\)\(01\) 方阵,请计算其中 X 的数目。

X 定义为用 \(1\) 填充且形状为 X 的联通块。具体的,X 由左向斜线 \ 和右向斜线 / 构成,且需要保证左向斜线和右向斜线长度相等,而且 X 是中心对称图形,斜线长度大于 \(1\)

例如:

101
010
101

中有一个X;

1001
0110
0110
1001

中有两个斜线长度为4的X。

————————↑以上内容均为题目描述↑—————————

考虑如何做:对于中心点为 \(1\)\(1\) 的X,我们可以很容易地想到去枚举中心点,然后向外扩。

那么对于中心为一个块的怎么处理呢?

可以回想一下我们学manachar的时候,是在字符串中插入一些相同的占位符号,以保证每次都能找到一个对称中心。这道题我们也可以用相同的思路处理。也就是,在每一行和每一列之间插入一个字符,然后枚举中心点即可。

复杂度:$O_{(8n^3)} $

当然,可以优化的点很多。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 205;

char a[N][N], s[N][N];
int n;
int dx[4] = {-1, 1, -1, 1};
int dy[4] = {-1, 1, 1, -1};
int X[4], Y[4];
int main() {
	scanf("%d", &n);
	for(int i = 1; i<=n; i++) {
		scanf("%s", a[i]+1);
	}
	for(int i = 1; i<=n; i++) {

		for(int j = 1; j<=n; j++) {
			s[(i<<1)-1][(j<<1)-1] = a[i][j];
			s[(i<<1)-1][j<<1] = '$';
		}

		for(int j = 1; j<=2*n-1; j++) {
			s[i<<1][j] = '$';
		}

	}
	n<<=1;
	for(int i = 0; i<=n; i++) {
		s[0][i] = s[n][i] = '#';
		s[i][0] = s[i][n] = '#';//边界标志。 
	}
	n--;
	long long ans = 0;
	for(int i = 1; i<=n; i++) {
		for(int j = 1; j<=n; j++) {
			for(int k = 1; k<=n; k++) {
				if(s[i][j] == '0') {
					continue;
				}//注意:中间点为0的时候不能扩展qwq否则只有20分 
				X[0] = i+k*dx[0], Y[0] = j+k*dy[0];
				X[1] = i+k*dx[1], Y[1] = j+k*dy[1];
				X[2] = i+k*dx[2], Y[2] = j+k*dy[2];
				X[3] = i+k*dx[3], Y[3] = j+k*dy[3];
				if((s[X[0]][Y[0]] == '#') || (s[X[1]][Y[1]] == '#') || (s[X[2]][Y[2]] == '#') || (s[X[3]][Y[3]] == '#')) {
					break;
				}
				if((s[X[0]][Y[0]] == '0') || (s[X[1]][Y[1]] == '0') || (s[X[2]][Y[2]] == '0') || (s[X[3]][Y[3]] == '0')) {
					break;
				}
				if((s[X[0]][Y[0]] == '1') && (s[X[1]][Y[1]] == '1') && (s[X[2]][Y[2]] == '1') && (s[X[3]][Y[3]] == '1')) {
					ans++;
				}
			}
		}
	}
	printf("%lld\n", ans);
	return 0;
}

T2王座下的背叛纲领

这个题我最后一秒写完的,没交上……

简化题意:给定一个 \(w \times h\) 的方阵,每次可以将一个点及其上下左右四个点异或 \(1\),求将矩阵内元素全变成 \(1\) 的最少的操作次数。

说一说我拿到题的思路:我一开始没啥特别好的思路,但是看到 \(w\) 很小,似乎可以状压解决,而且这个题貌似很像之前做过的P3977棋盘。并且因为是异或,所以一个点改变两次状态是没有意义的。于是我就考虑状压dp。我先搜出来了一行的所有操作情况及其对应的状态改变,但是在转移的时候发现总是会把下一个状态算重。

这时候,我发现一个很重要的性质:就是,当你的上一行的状态确定时,你的下一行的操作情况也是确定的。因为你必须保证上一行全变成 \(1\)!那么,你的下一行所对应的操作就是去变换上一行状态中 \(0\) 所对应的位置。

很遗憾,发现这个性质后我没有好好利用,还是去想dp。最后才想到,其实对于第一行的每个初始状态,都有最多一个确定的方案去变换矩阵中的所有元素。那么,我们只需要枚举第一行的状态,然后往下搜索即可。注意,只有最后一行的终状态全为 \(1\),我们才能统计答案。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 1200, H = 1210;

int T;
int w, h;
int fst[H];


int tsf[N], aft[N], opr[N], tot;
//分别对应:变换方式(1为变换,0为不变),对应的最终结果(1为异或1,0为不异或),操作次数,方案总数。
int f_tsf[N];//某一状态对应的方案
void dfs(int pos, int x, int y, int op) {
	if(pos>=w) {
		tsf[++tot] = x;
		aft[tot] = y;
		opr[tot] = op;
		f_tsf[x] = tot;
		return;
	}
	dfs(pos+1, x, y, op);
	if(w == 1) {
		dfs(pos+1, (x|(1<<pos)), y^1, op+1);
	} else if(pos<1) {
		dfs(pos+1, (x|(1<<pos)), (y^3), op+1);
	} else if(pos == w-1) {
		dfs(pos+1, (x|(1<<pos)), (y^(3<<(pos-1))), op+1);
	} else {
		dfs(pos+1, (x|(1<<pos)), (y^(7<<(pos-1))), op+1);
	}

}
int mx;
void print_fst(int aa[], int bb[]) {
	for(int i = 1; i<=tot; i++, puts("")) {
		for(int j = 0 ; j<w; j++) {
			printf("%d", ((aa[i]>>j)&1));
		}
		printf(" ");
		for(int j = 0; j<w; j++) {
			printf("%d", ((bb[i]>>j)&1));
		}
	}
}
long long ans;
void init() {
	ans = 0x3f3f3f3f3f3f3f3f;
	memset(opr, 0x3f, sizeof(opr));
	memset(fst, 0, sizeof(fst));
	tot = 0;
	memset(tsf, 0, sizeof(tsf));
	memset(aft, 0, sizeof(aft));

}

void dfs2(int pos, int lst,  long long sum, int opp) {

	if(pos>h) {
		if(lst == mx) ans = min(sum, ans);
		return;
	}

	int ned = lst^mx;
	int tmp = f_tsf[ned];
	dfs2(pos+1, fst[pos]^tsf[opp]^aft[tmp], sum+opr[tmp], tmp);



}
int main() {
	scanf("%d", &T);
	while(T--) {
		init();
		scanf("%d%d", &w, &h);
		for(int i = 0; i<w; i++) {
			for(int j = 1; j<=h; j++) {
				char tmp[2];
				scanf("%s", tmp);
				if(tmp[0] == 'A') {
					fst[j]|=(1<<i);
				}
			}
		}
		mx = (1<<w)-1;
		dfs(0, 0, 0, 0);
		for(int i = 1; i<=tot; i++) {
			dfs2(2, fst[1]^aft[i], opr[i], i);
		}

		printf("%lld\n", ans);

	}

	return 0;
}
posted @ 2023-06-07 10:44  霜木_Atomic  阅读(38)  评论(0编辑  收藏  举报