1-4金币阵列问题

Posted awcxv

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1-4金币阵列问题相关的知识,希望对你有一定的参考价值。

问题描述:
有m ′ n(m £ 100,n £ 100) 个金币在桌面上排成一个m 行 n 列的金币阵列。每一枚金
币或正面朝上或背面朝上。用数字表示金币状态,0 表示金币正面朝上,1 表示背面朝上。
金币阵列游戏的规则是:
(1)每次可将任一行金币翻过来放在原来的位置上;
(2)每次可任选 2 列,交换这 2 列金币的位置。
′编程任务:
给定金币阵列的初始状态和目标状态,编程计算按金币游戏规则,将金币阵列从初始状
态变换到目标状态所需的最少变换次数。
′数据输入:
由文件 input.txt 给出输入数据。文件中有多组数据。文件的第 1 行有 1 个正整数 k,表
示有 k 组数据。每组数据的第 1 行有 2 个正整数 m 和 n。以下的 m 行是金币阵列的初始状
态,每行有 n 个数字表示该行金币的状态,0 表示金币正面朝上,1 表示背面朝上。接着的
m 行是金币阵列的目标状态。
′结果输出:
将计算出的最少变换次数按照输入数据的次序输出到文件 output.txt。相应数据无解时
输出-1。
输入文件示例 输出文件示例
input.txt output.txt
2
4 3
1 0 1
0 0 0
1 1 0
1 0 1
1 0 1
1 1 1
0 1 1
1 0 1
4 3
1 0 1
0 0 0
1 0 0
1 1 1
1 1 0
1 1 1
0 1 1
1 0 1
2
-1

【题解】


我们假定最后的答案中a数组中的第j列和b数组中的第一列相同
那么我们就先用行取反操作真的把a数组中的第j列变成和b数组的第一列一样。
这个时候你会发现,你再也不能用行取反操作了.
只剩下列交换操作可以用了。
那么只需要把b数组和a数组变成n个列向量(用字符串表示),然后把两个数组都按照字符串排个序就好(字符串的字典序).
如果两个字符串数组完全相同,那么就可以从a变换到b
记录下a的第i列要换到b的第几列就可以了。
然后就变成另外一个问题了:
已知位置i上的数字最后要到next[i]位置。只能进行交换操作,问最少要操作几次才能满足所有的i(1<=i<=n)
这个个经典问题了,最后会形成若干个环,∑(环的大小-1)就是操作步骤了,再加上之前取反操作的次数就是a的第j列和b的第一列相同的情况下的答案了

【代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

struct abc
    string s;
    int column;
;

const int N = 100;

int T,m,n;
int a[N+10][N+10],b[N+10][N+10];
abc bcs[N+10];
int aa[N+10][N+10];
int minstep = -1;

bool cmp(abc a,abc b)
    return a.s<b.s;


void fz(int r)
    for (int i = 1;i <= n;i++)
        aa[r][i] = 1-aa[r][i];
    




int judge(int idx)

    int step = 0;
    for (int i = 1;i <= m;i++)
        if (aa[i][idx]!=b[i][1])
            fz(i);
            step++;
        
    
    //把aa的每一列看成一个长度为m的01字符串,即n个字符串
    abc acs[N+10];
    int nex[N+10];//记录aa的每一列需要变换到哪一列
    for (int j = 1;j <= n;j++)
        string temp = "";
        for (int i = 1;i <= m;i++)
            temp = temp + (char)(aa[i][j]+'0');
        
        acs[j].column = j;
        acs[j].s = temp;
    
    sort(acs+1,acs+1+n,cmp);
    //for (int i = 1;i <= n;i++) cout<<acs[i].s<<" ";cout<<endl;
    //for (int i = 1;i <= n;i++) cout<<bcs[i].s<<" ";
    //exit(0);
    for (int i = 1;i <= n;i++)
        if (acs[i].s!=bcs[i].s)
            return -1;
        else 
            nex[acs[i].column] = bcs[i].column;
        
    int flag[N+10];
    for (int i = 1;i <= n;i++) flag[i] = -1;
    for (int i = 1;i <= n;i++)
        if (flag[i]==-1)
            int j = i;
            int cnt = 0;
            while (flag[j]==-1)
                flag[j] = 1;
                cnt++;
                j = nex[j];
            
            step+=cnt-1;
        
    return step;


int main()
    //freopen("E://tran5.in","r",stdin);
    cin >> T;
    while (T--)
        minstep = -1;
        cin >> m >> n;
        for(int i = 1;i <= m;i++)
            for (int j = 1;j <= n;j++)
                cin >> a[i][j];
        for(int i = 1;i <= m;i++)
            for (int j = 1;j <= n;j++)
                cin >> b[i][j];
        //将b转换成n个列向量
        for (int j = 1;j <= n;j++)
            string temp="";
            for (int i = 1;i <= m;i++)
                temp = temp + (char)(b[i][j]+'0');
            
            bcs[j].s = temp;
            bcs[j].column = j;
        
        sort(bcs+1,bcs+1+n,cmp);

        for (int j = 1;j <= n;j++)//suppose j column equal to first column of b
            memcpy(aa,a,sizeof(a));
            int t = judge(j);
            if (t==-1) continue;
            if (minstep==-1)
                minstep = t;
            else minstep = min(minstep,t);
        
        cout<<minstep<<endl;
    
    return 0;

以上是关于1-4金币阵列问题的主要内容,如果未能解决你的问题,请参考以下文章

算法概述-习题

骑士得到金币问题

检查金币

求换取零钱的最少金币个数个数--动态规划问题2

组合游戏与博弈

noip模拟赛 捡金币