[AcWing 116]飞行员兄弟 题解
题目链接:[https://www.acwing.com/problem/content/118/]
题目大意
给定一个由 '-' 和 '+' 组成的 4x4 的矩阵
对点 (i , j) 的一次操作会使得第 i 行和第 j 列的符号全部发生改变 ('-' -> '+','+' -> '-')
求出一个操作方案,使得每个点都为 '-' 且操作的次数最少
若有多种操作方案,则按照优先级整体从上到下,同行从左到右,使得方案的优先级最高
思路
看到这道题,我下意识地认为需要使用递推解决
于是就想了好久,怎样用上一行的状态改变下面
后来发现,这道题只有 16 个可一进行操作的位置,且容易发现,每个位置最多只能被操作一次
所以这道题就转化为了:在 16 个位置中,选出 x(1 <= x <= 16) 个位置
使得对这 x 个位置进行操作后,所有的点对应的符号都变为 '-',并且这个方案的“优先级”最高
那么,这道题就变得非常简单了,我们可以通过dfs或者2进制枚举每一种方案,判断这种方案的可行性
若可行,按照题目要求更新最佳方案即可
由于本人正在练习位运算,所以采用位运算枚举
code
include cstdio //博客园的抽风特效会把代码吞掉,所以去掉了井号和书名号
include cstring
include algorithm
include utility
using namespace std;
const int n = 4;
typedef pair pa;
char g[5][5],s[5][5];
int lg[1 << 16];
char readchar() {
char c = getchar();
while(c != '-'&&c != '+')c = getchar();
return c;
}
void change(int x,int y) {
if(g[x][y] == '-')g[x][y] = '+';
else g[x][y] = '-';
}
pa get(int x) {//把整数x映射为矩阵上点的下标 eg.1 -> (1,1) 4 -> (1,4) 15 -> (4 , 3)
++ x;//因为在下面的调用中,直接调用了二进制的位数,比正常的位置少了1,所以要+1
int a = x / n,b = x - n * a;
if(!b) {
-- a;
b = n;
}
return make_pair(a + 1 , b);
}
void turn(int x,int y) {
for(int i = 1;i <= n;++ i) {
change(x , i);
change(i , y);
}
change(x , y);//这里是重点,因为这个位置会被操作两遍,相当于没操作
//所以要在循环外面手动再操作一遍
return ;
}
bool resc() {//判断是否达到目标状态
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= n;++ j) {
if(g[i][j] == '+')return false;
}
}
return true;
}
int main() {
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= n;++ j) {
g[i][j] = readchar();
}
}
int mx = 16,best = (1 << 16) - 1;
for(int i = 0;i <= (1 << 16) - 1;++ i) {
int cnt = 0;
memcpy(s , g , sizeof(g));//保存原数组
for(int j = 0;(1 << j) <= i;++ j) {
if(i >> j & 1) {
++ cnt;//这个方案里操作的个数
pa p = get(j);
turn(p.first , p.second);
}
}
if(resc()) {//达到目标
if(mx > cnt) {
mx = cnt;
best = i;
}
if(mx == cnt) {
if(i < best) {//借助二进制数的位数特点,直接通过比大小确定优先级
best = i;
}
}
}
memcpy(g , s , sizeof(s));//回溯操作 可以不使用memcpy,手动复原
}
printf("%d\n",mx);
for(int i = 0;i <= 15;++ i) {//输出方案
if(best & (1 << i)) {
pa p = get(i);
printf("%d %d\n",p.first,p.second);
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
感谢大家(▽)