AcWing 116. 飞行员兄弟
“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱。
已知每个把手可以处于以下两种状态之一:打开或关闭。
只有当所有把手都打开时,冰箱才会打开。
把手可以表示为一个4х4的矩阵,您可以改变任何一个位置[i,j]上把手的状态。
但是,这也会使得第i行和第j列上的所有把手的状态也随着改变。
请你求出打开冰箱所需的切换把手的次数最小值是多少。
输入格式
输入一共包含四行,每行包含四个把手的初始状态。
符号“+”表示把手处于闭合状态,而符号“-”表示把手处于打开状态。
至少一个手柄的初始状态是关闭的。
输出格式
第一行输出一个整数N,表示所需的最小切换把手次数。
接下来N行描述切换顺序,每行输入两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。
注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。
数据范围
输入样例:
输出样例:
6
1 1
1 3
1 4
4 1
4 3
4 4
题目分析:
首先,这道题目的数据范围很小,输入的固定是一个4*4的矩阵。
我们对每一个把手可以采取按或者不按两种方案,所以就算是暴力法来做,也只是的复杂度,大概是65536次。我们用一个整数来表示状态,每次遍历16次,去判断这16个把手按或者不按。
每次按的时候,我们通过异或一个数,来做到改变某一行某一列的状态。
比如我们遍历状态i发现,第3位是1,那么第零行,第三列就都需要改变状态,即需要异或的数是
,我们提前准备一个change数组来存储按某个把手需要异或的数。
然后找出这65536种状态里面,满足条件的状态是哪个,即最后结果为0的。
代码如下:
#include<iostream>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
int change[4][4];
int get(int x,int y)
{
return x*4+y;
}
int main()
{
int state=0;
for(int i=0;i<4;i++)
{
string str;
cin>>str;
for(int j=0;j<4;j++)
{
if(str[j]=='+')
state+=1<<get(i,j);
}
}
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
for(int k=0;k<4;k++)
{
change[i][j]+=1<<get(i,k);
change[i][j]+=1<<get(k,j);
}
change[i][j]-=1<<get(i,j);
}
vector<PII> res;
for(int i=0;i<1<<16;i++)
{
int now=state;
vector<PII> path;
for(int k=0;k<16;k++)
{
if(i>>k&1)
{
int x=k/4;
int y=k%4;
now^=change[x][y];
path.push_back({x,y});
}
}
if(!now&&(res.empty()||res.size()>path.size())) res=path;
}
cout<<res.size()<<endl;
for(int i=0;i<res.size();i++)
{
cout<<res[i].first+1<<" "<<res[i].second+1<<endl;
}
return 0;
}
AcWing 117. 占卜DIY
达达学会了使用扑克DIY占卜。
方法如下:
一副去掉大小王的扑克共52张,打乱后均分为13堆,编号1~13,每堆4张,其中第13堆称作“生命牌”,也就是说你有4条命。
这里边,4张K被称作死神。
初始状态下,所有的牌背面朝上扣下。
流程如下:
1.抽取生命牌中的最上面一张(第一张)。
2.把这张牌翻开,正面朝上,放到牌上的数字所对应编号的堆的最上边。(例如抽到2,正面朝上放到第2堆牌最上面,又比如抽到J,放到第11堆牌最上边,注意是正面朝上放)
3.从刚放了牌的那一堆最底下(最后一张)抽取一张牌,重复第2步。(例如你上次抽了2,放到了第二堆顶部,现在抽第二堆最后一张发现是8,又放到第8堆顶部.........)
4.在抽牌过程中如果抽到K,则称死了一条命,就扔掉K再从第1步开始。
5.当发现四条命都死了以后,统计现在每堆牌上边正面朝上的牌的数目,只要同一数字的牌出现4张正面朝上的牌(比如4个A),则称“开了一对”,当然4个K是不算的。
6.统计一共开了多少对,开了0对称作”极凶”,12对为“大凶”,3对为“凶”,45对为“小凶”,6对为“中庸”,78对“小吉”,9对为“吉”,1011为“大吉”,12为“满堂开花,极吉”。
输入格式
一共输入13行数据,每行四个数字或字母,表示每堆牌的具体牌型(不区分花色只区分数字),每堆输入的顺序为从上到下。
为了便于读入,用0代表10。
同行数字用空格隔开。
输出格式
输出一个整数,代表统计得到的开出的总对数。
输入样例:
8 5 A A
K 5 3 2
9 6 0 6
3 4 3 4
3 4 4 5
5 6 7 6
8 7 7 7
9 9 8 8
9 0 0 0
K J J J
Q A Q K
J Q 2 2
A K Q 2
输出样例:
9
题目分析:
首先,要对输入进行处理,把所有的输入处理之后存在13个vector中,每个vector中放4个数据。
输入A,表示是1。
输入2-9,表示2-9。
输入0,表示10。
输入J、Q、K,分别表示11、12、13。
第一次,将13首位的数拿出来,开始循环,直到取到的数是13,再从新从13中拿下一个数。
其中,循环取得数是对应的数组的最后一个数,每次取了之后,将最后一个数去掉。
代码如下:
#include<iostream>
#include<vector>
using namespace std;
const int N=14;
vector<int> cards[N];
int arr[N];
int main()
{
for(int i=1;i<=14;i++)
{
for(int j=0;j<4;j++)
{
char c;
cin>>c;
if(c=='0') cards[i].push_back(10);
else if(c=='A') cards[i].push_back(1);
else if(c<='9') cards[i].push_back(c-'0');
else if(c=='J') cards[i].push_back(11);
else if(c=='Q') cards[i].push_back(12);
else if(c=='K') cards[i].push_back(13);
}
}
int res=0;
int i=0;
while(i!=4)
{
int x=13;
x=cards[x][i];
while(x!=13)
{
arr[x]++;
if(arr[x]==4) res++;
int t=cards[x].back();
cards[x].pop_back();
x=t;
}
arr[13]++;
i++;
}
cout<<res<<endl;
return 0;
}