Acwing 算法竞赛进阶指南打卡行动 第一章小结

AcWing 116. 飞行员兄弟

“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱。

已知每个把手可以处于以下两种状态之一:打开或关闭。

只有当所有把手都打开时,冰箱才会打开。

把手可以表示为一个4х4的矩阵,您可以改变任何一个位置[i,j]上把手的状态。

但是,这也会使得第i行和第j列上的所有把手的状态也随着改变。

请你求出打开冰箱所需的切换把手的次数最小值是多少。

输入格式

输入一共包含四行,每行包含四个把手的初始状态。

符号“+”表示把手处于闭合状态,而符号“-”表示把手处于打开状态。

至少一个手柄的初始状态是关闭的。

输出格式

第一行输出一个整数N,表示所需的最小切换把手次数。

接下来N行描述切换顺序,每行输入两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。

注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。

数据范围

1≤i,j≤4

输入样例:

输出样例:

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

题目分析:

首先,这道题目的数据范围很小,输入的固定是一个4*4的矩阵。
我们对每一个把手可以采取按或者不按两种方案,所以就算是暴力法来做,也只是2^{16}的复杂度,大概是65536次。我们用一个整数来表示状态,每次遍历16次,去判断这16个把手按或者不按。
每次按的时候,我们通过异或一个数,来做到改变某一行某一列的状态。
比如我们遍历状态i发现,第3位是1,那么第零行,第三列就都需要改变状态,即需要异或的数是
2^0+2^1+2^2+2^3+2^7+2^11+2^15,我们提前准备一个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;
    
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。