题目描述:
给出4×4共16个门把手,改变一个门把手(打开或关闭)需要同时改变同行同列的门把手,当所有门把手都打开时才能打开门。+代表关,-代表开。
Solution
说实话,我刚开看到这道题的思路就是状压dp+最短路
但后面想着,这个最短路莫得办法连边。。除非一张图连个16条边出去。
但这很显然不现实。
那么这时候我们就要思考一个问题:如何让一个’+‘变成’-’,并且让他同行同列的点不受影响?
- 我们可以得到这样一个性质:一个点如果被改变偶数次,那么这个点的状态是不会改变的
- 也就是说我们希望处于 ( i , j ) (i,j) (i,j)位置上的’+'的改变次数为奇数但是与他同行同列的点的改变次数为偶数
- 因为一个点改变了会是他这一行与他这一列的改变次数都+1,所以我们考虑把与 ( i , j ) (i,j) (i,j)位置上同行同列的数字都改变一次,这样便满足了上面的条件
- 为什么呢?
- 感性理解一番,与 ( i , j ) (i,j) (i,j)位置上相关的点(包括 ( i . j ) (i.j) (i.j))一共有7个。所以全都改变了会使 ( i , j ) (i,j) (i,j)的位置发生改变。又由于(i,j)所同行同列的点改变后其实会使棋盘上剩余的点的改变次数发生改变,但这棋盘上的每一个点的行与列都对应着(i,j)同行同列上的一个点。所以棋盘上的剩余的点的次数不会发生变化。
- 所以这是可行的
那么答案如何记录呢?
- 根据一个点被改变偶数次就相当于不改变的性质,我们只需要记录整个棋盘上改变了奇数次的点即可
Code
#include<bits/stdc++.h>
using namespace std;
#define gc getchar()
int a[101][101];
int ans = 0;
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
for (int i=1;i<=4;i++)
for (int j=1;j<=4;j++){
char ch=gc;
while (ch!='+'&&ch!='-') ch=gc;
if (ch=='+'){
a[i][j]-=1;
for (int k=1;k<=4;k++) a[i][k]++,a[k][j]++;
}
}
for (int i=1;i<=4;i++)
for (int j=1;j<=4;j++)
ans+=a[i][j]%2;
printf("%d\n",ans);
for (int i=1;i<=4;i++)
for (int j=1;j<=4;j++)
if (a[i][j]%2) printf("%d %d\n",i,j);
return 0;
}