20-2-5-状圧枚举-POJ2965

POJ2965

The Pilots Brothers’ refrigerator

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 33516Accepted: 12994Special Judge

Description

The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to open a refrigerator.There are 16 handles on the refrigerator door. Every handle can be in one of two states: open or closed. The refrigerator is open only when all handles are open. The handles are represented as a matrix 4х4. You can change the state of a handle in any location [i, j] (1 ≤ i, j ≤ 4). However, this also changes states of all handles in row i and all handles in column j.The task is to determine the minimum number of handle switching necessary to open the refrigerator.

Input

The input contains four lines. Each of the four lines contains four characters describing the initial state of appropriate handles. A symbol “+” means that the handle is in closed state, whereas the symbol “−” means “open”. At least one of the handles is initially closed.

Output

The first line of the input contains N – the minimum number of switching. The rest N lines describe switching sequence. Each of the lines contains a row number and a column number of the matrix separated by one or more spaces. If there are several solutions, you may give any one of them.

Sample Input

-+--
----
----
-+--

Sample Output

6
1 1
1 3
1 4
4 1
4 3
4 4

Source

Northeastern Europe 2004, Western Subregion

  • 暴力枚举
  • 一个深搜结局了…
    • 16个位置, 改变或者不改变向下搜索
  • 要骚解法?—>此处是骚解法
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int maxn = 25;
const int INF = 0x3f3f3f3f;
int g[5][5];
int tmp_x[maxn];
int tmp_y[maxn];
int ans_pathx[maxn];
int ans_pathy[maxn];
int ans = INF;

void traverse(int pos){
    int tx = pos / 4, ty = pos % 4;
    g[tx][ty] = !g[tx][ty];
    for (int i = 0; i < 4; i++){
        g[i][ty] = !g[i][ty];
        g[tx][i] = !g[tx][i];
    }
}

bool ok(){
    for (int i = 0; i < 4; i++){
        for (int j = 0; j < 4; j++)
            if (g[i][j] == 0) {
                return false;
            }
    }
    return true;
}

//遍历到了第pos个, 翻转了traverse_num个
void dfs(int pos, int traverse_num){
    if (ok()){
        if (ans > traverse_num){
            ans = traverse_num;
            for (int i = 0; i < ans; i++){
                ans_pathx[i] = tmp_x[i], ans_pathy[i] = tmp_y[i];
            }
        }
        return;
    }
    if (pos >= 16) {
        return;
    }
    //第pos个不翻转
    dfs(pos + 1, traverse_num);

    //翻转存入g中
    traverse(pos);
    tmp_x[traverse_num] = pos / 4 + 1;
    tmp_y[traverse_num] = pos % 4 + 1;
    dfs(pos + 1, traverse_num + 1);
    traverse(pos);
}

int main(){
    char c;
    memset(g, 0, sizeof(g));
    for (int i = 0; i < 4; i++){
        for (int j = 0; j < 4; j++) {
            cin >> c;
            if (c == '-') {
                g[i][j] = 1;
            }
            else {
                g[i][j] = 0;
            }
        }
    }
    dfs(0, 0);
    cout << ans << "\n";
    for (int i = 0; i < ans; i++){
        printf("%d %d\n", ans_pathx[i], ans_pathy[i]);
    }
    return 0;
}
  • 原本想改改POJ1765的代码的, 改了半天, 还T了. 这个代码写的复杂了, 贴在这留个纪念.

    #pragma warning(disable:4996)
    #include <cstring>
    #include<iostream>
    
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int maxn = 10;
    const int MAXN = 100000;
    //每个地方的字符有改和不改的两种情况
    	//故一共2**16种状态
    //每个地方的字符改一次和两次和若干次取得的效果相同
    //采用状态压缩, 一个状态压缩为一个int
    char g[maxn][maxn];
    
    int path[17];
    int  ans[17];
    
    //当前的值cur, 需要翻转的个数times, 当前光标所在的位置pos, 当前已翻转的个数cur_times
    bool traverse(int cur, int times, int pos, int cur_times) {
    	if (cur_times == times) {
    		if (cur == 0) {
    			for (int i = 0; i < 16; i++) {
    				ans[i] = path[15 - i];
    			}
    			return true;
    		}
    		return false;
    	}
    	if (pos >= 16) {
    		return false;
    	}
    	int curr;
    	for (int i = pos; i < 16; i++) {
    		curr = cur ^ (1 << i);
    		path[i] = 1;
    		int tmp[5][5];
    		int cnt = 0;
    		for (int i = 3; i >= 0; i--) {
    			for (int j = 3; j >= 0; j--) {
    				tmp[i][j] = !!(curr & (1 << cnt));
    				cnt++;
    			}
    		}
    		int x, y;
    		x = i / 4;
    		y = i % 4;
    		x = 3 - x;
    		y = 3 - y;
    		//那一列的翻转
    		for (int i = 0; i < 4; i++) {
    			tmp[i][y] = tmp[i][y] ^ 1;
    		}
    		//那一行的翻转
    		for (int i = 0; i < 4; i++) {
    			tmp[x][i] = tmp[x][i] ^ 1;
    		}
    		curr = tmp[0][0] == 1 ? 1 : 0;
    		/*	for (int i = 0; i < 4; i++) {
    				for (int j = 0; j < 4; j++) {
    					cout << tmp[i][j] << " ";
    				}
    				cout << endl;
    			}*/
    			//将数组中的值组合成一个int
    		for (int i = 0; i < 4; i++) {
    			for (int j = 0; j < 4; j++) {
    				if (i + j == 0) {
    					continue;
    				}
    				curr <<= 1;
    				curr += tmp[i][j];
    			}
    		}
    		if (traverse(curr, times, i + 1, cur_times + 1)) {
    			return true;
    		}
    		path[i] = 0;
    	}
    	return false;
    }
    
    //关闭的'+'用1
    int solve() {
    	int cur = g[0][0] == '+' ? 1 : 0;
    	for (int i = 0; i < 4; i++) {
    		for (int j = 0; j < 4; j++) {
    			if (i + j == 0) {
    				continue;
    			}
    			//左移一位
    			cur <<= 1;
    			if (g[i][j] == '+') {
    				cur += 1;
    			}
    		}
    	}
    	if (cur == 0) {
    		return 0;
    	}
    	//
    	int last = cur;
    	//改变的开关的个数, last为当前开关的状态
    	for (int i = 1; i <= 16; i++) {
    		if (traverse(last, i, 0, 0)) {
    			return i;
    		}
    	}
    	return -1;
    }
    int main() {
    	char c;
    	for (int i = 0; i < 4; i++) {
    		for (int j = 0; j < 4; j++) {
    			cin >> c;
    			g[i][j] = c;
    		}
    	}
    	int res = solve();
    	cout << res << "\n";
    	for (int i = 0; i < 16; i++) {
    		if (ans[i] == 1) {
    			cout << i / 4 + 1 << " " << i % 4 + 1;
    			cout << "\n";
    		}
    	}
    	return 0;
    }
    /*
    -+--
    ----
    ----
    -+--
    */
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值