Windows 10下使用Windows+Shift+S截图

好消息,好消息!如果你正在使用较新的Windows 10,那么截图只要:Windows徽标键+Shift+S即可触发截图和草图应用来抓取矩形截图、任意形状截图、窗口截图或是全屏幕截图Windows徽标键+PrtSc即可触发截图和草图应用直接抓取全屏幕截图不用再为了截图打开微信或QQ啦。

截图和草图应用

如果你觉得这个快捷键比较难按,你可以在设置->轻松使用->键盘中修改:

修改热键为PrtScn

什么?你说我火星了?好像不是我,是我的朋友们…(无中生友ing)

好,来说说这个功能。根据此文章Why doesn’t the screen clipping tool work anymore? – OneNote所述,该热键本是OneNote用户用于截图的,自Windows 10创意者更新(也就是1703)后,由截图与草图应用接管。相关文章还包括:What’s New in Windows 10’s Creators UpdateHow to take and annotate screenshots on Windows 10

还要注意的是,根据Snip & Sketch…. : Windows10 – Reddit。截取屏幕后,你通常直接从剪贴板中取得图片,但是在:%LOCALAPPDATA%\Packages\Microsoft.Windows.ShellExperienceHost_cw5n1h2txyewy\TempState\ScreenClip路径下仍然保存有图片的副本(也就是C:\Users\%USERNAME%\AppData\Local\Packages\Microsoft.Windows.ShellExperienceHost_cw5n1h2txyewy\TempState\ScreenClip)。可能有隐私泄露风险,请务必注意。

普通用户不需要sudo使用Docker

大多数情况下,普通用户使用Docker都需要使用sudo进行提权,否则可能要切换到root用户才可直接使用。其实只需要将当前用户加入docker用户组即可。

The docker group grants privileges equivalent to the root user. For details on how this impacts security in your system, see Docker Daemon Attack Surface.

Warning from Post-installation steps for Linux | Docker Documentation

确实有风险,可能导致利用Docker提权之类的问题。搞事之前一定要明白自己在做什么。

正常情况下应该已经存在docker用户组了,如果没有则需要:

sudo groupadd docker

来添加一个名为docker的用户组。

然后就可以将当前用户加入docker用户组:

sudo usermod -aG docker $USER

或者:

sudo gpasswd -a $USER docker

注销后重新登录即可生效。按文档,对于有图形界面的Linux,应该注销再登录即可,否则应该完全重启。如果不方便注销,可以尝试:

newgrp docker

此时应该可以不需要sudo来使用Docker了。

发现版本19.03起有一个实验性特性,以非root用户运行Docker守护进程:

dockerd-rootless.sh --experimental

引用:
Post-installation steps for Linux | Docker Documentation
How can I use docker without sudo? – Ask Ubuntu
Docker security | Docker Documentation

VLAN Lab

让队友和我一起折腾到了六点多,有点愧疚,感谢各位大佬支持。

记录一下踩雷的历史,希望对各位有所帮助,如果文中存在差错,还望各位指点。写这些的目的并不是为了让某些懒人直接照搬进去实验报告的,我还是希望能够营造一个大家积极交流而进步的氛围,而不是放任有些人能混过去就行的心态,让开源给抄袭提供便利。如果是这样的话我宁愿自己删掉这些文章。

这次的环境是:

  • 机房的电脑4台,全部是Windows 7系统。其中一台电脑使用Xshell,通过串口连接并配置交换机。
  • Quidway S5700系列交换机1台,Quidway S3700系列交换机1台。

…并不是我故意找了两台不一样的,而是我所在的位置上的机柜里就是这样的。这样的情况带来的问题就是有些命令不大一样,需要先翻手册才知道。当然你要是因为这个就黑说老师的PPT不对就有点过了,PPT上面的内容个人认为最多算是启发性质的、大方向上的教程,并且针对的交换机型号、硬件版本、固件版本不能总是和你所在的环境一样,更多的细节上的问题你总是需要自己想办法去解决和适应。

画个重点:2019年底的时候,计软3楼某些实验室已经更换了全套全新的交换机和路由器,颇有财大气粗、大材小用的气势。至于为什么这么说,等你去到实验室感受一下就知道了。我觉得这个时候你大概不会再需要头疼我遇到的很多问题了。

CONSOLE登录

为了配置交换机,这里你需要用串口来控制你的交换机。以前如果你有折腾过普通家用的路由器/网管交换机,你通常都是访问路由器的Web管理界面(例如浏览器访问http://192.168.1.1/)来进行配置,现在就不一样了。原因比较多,就不在这里废话了。

正常情况下,你应该可以看到交换机上面的CONSOLE口有一根线连到了某台电脑的RS-232(这个是我瞎说的)口上,这个时候就用这台电脑来配置交换机吧。

PPT里的示例使用的是超级终端,不过我使用的是Xshell,像这样子新建一个连接,然后选择串口SERIAL

VLAN_LAB_1.jpg
Xshell 新建会话

先别急着确定,我一般会习惯性地先看看端口号:

VLAN_LAB_2.jpg
右击此电脑->管理->设备管理器

COM1没错了,记住它,到左侧的SERIAL中确认一下各项参数是否正确,然后就可以连接了。

VLAN_LAB_3.jpg
Xshell 连接成功

连接上之后,可以直接输入一个问号?来查看帮助。平时输入命令的时候,可以用TAB来提示相关词,这个就是键盘上那个制表符,我强烈建议你在控制台里多利用提示和补全,而不是YY。

然后根据实验报告的要求,你可以到各种视图中转悠转悠熟悉一下操作:

VLAN_LAB_4.jpg
进入system-view

什么?你说PPT上的端口不对?你看过文档或是display看过了吗?

VLAN_LAB_5.png
端口要具体情况具体分析

这里就不再多说什么了,端口各种配置各位就自行看文档发掘吧。

VLAN基本配置

实验要求大概是这样的:

VLAN_LAB_6.png
实验要求 图源PPT

按图连接好实验设备,PC1、PC2、交换机的IP地址分别为10.110.10.1、10.110.10.2、10.110.10.3,子网掩码均设置为255.255.255.0。建立VLAN2、VLAN3,通过配置将端口Ethernet 0/3包含到VLAN2中,将端口Ethernet 0/4包含到VLAN3中,并使用PING命令检查VLAN工作情况,测试PC1、PC2、交换机之间能否PING成功。
将端口Ethernet 0/4包含到VLAN2中,并使用PING命令检查VLAN工作情况,测试PC1、PC2 、交换机之间能否PING成功。

实验要求 源PPT

简单来讲就是要把两台PC设置在不同VLAN中,然后测试一下它们在没有其它因素的时候能不能通信。(废话嘛…这要是可以还要VLAN干吗哦…)

所以我们先把PC1、PC2分别连接到交换机的Ethernet 0/0/3、Ethernet 0/0/4上。正常情况下,连接正确的时候你能看到交换机上对应数字的灯在闪烁。连接完成后我们在PC上设置一下IP地址和子网掩码,就像这样:

VLAN_LAB_7.png
手动设置IP地址

你应该能找到它在哪里吧…控制面板->网络和和共享中心->本地连接->属性->IPv4。我感觉可能还是很多同学不知道这个选项在哪,也不知道为啥平时自己通常都不需要这么设置就能上网,这个主要是因为平时你所处的网络内都有DHCP服务器负责为你下发IP地址,只要PC是默认的自动获取IP地址,你通常都不需要干预就能正常上网,感兴趣的话我建议自己去查一下。

至于交换机的IP地址的设置…如果你以前没有接触过网管交换机的话,你可能会觉得奇怪:这不是二层设备么,为什么会有IP地址?你需要在vlanif上配置它,有关详细信息,你可以参阅这里:

vlanif和vlan的区别

交换机要ip有什么用? – 知乎

VLAN_LAB_8.png

接下来我们该来配置一下VLAN了。根据要求,把Ethernet 0/0/3包含到VLAN 2中,把Ethernet 0/0/4包含到VLAN 3中:

VLAN_LAB_9.jpg
摘自文档
VLAN_LAB_10.jpg
接口Ethernet0/0/03划分至VLAN 2

在VLAN中添加端口也是可行的。

如果遇到问题需要更改端口类型:

VLAN_LAB_11.png

如果你不明白,我想你应该去搜索一下有关trunk/access/hybrid的内容。

另一个接口同理,都配置好之后应该能看到是这样的:

VLAN_LAB_12.jpg
display vlan

这个时候就可以开始测试了:

VLAN_LAB_13.jpg
10.110.10.1发送ping至10.110.10.2

如果目标主机的防火墙默认阻挡ICMP协议的封包,请根据实际情况修改策略,例如接受局域网的ICMP封包,或者完全接受。你也可以为不联网的、与其它局域网隔离的实验的PC关闭防火墙。如果你不事先进行检查,这会影响到实验的结果。

由于两个PC被分配到了不同的VLAN中,并且没有可达路由,所以它们不能互相通信。这个时候,我们切换到Xshell中更改VLAN划分。由于刚刚的ping带有-t参数,它会不停地继续发送ping包。我们来把Ethernet 0/0/4改到VLAN2中看看:

VLAN_LAB_14.jpg
更改至同一VLAN后即可正常通信

我们刚把Ethernet 0/0/4添加到VLAN2后马上可以看到这边已经可以ping通了。

Trunking

希望你在搞这个之前已经清楚地知道自己在做什么…如果不知道的话,我觉得你可以尝试着去搜一下相关的内容,也可以看看单线复用、单臂路由之类的应用,或许能有所帮助,而且说不定就能在日常生活中用起来了呢?

我们先把之前创建的VLAN先都删掉:

VLAN_LAB_15.png
去掉刚刚设置的VLAN

这个时候还是接线,按照PPT说明接好:

VLAN_LAB_16.png

由于这个交换机的22好像是坏的…于是换到了20上(真是尴尬)。所以我们接下来要干的事情是:

  1. 将PC1连接到交换机1的Ethernet 0/0/3上,配置静态IP为10.110.10.1/24;
  2. 将PC2连接到交换机1的Ethernet 0/0/12上,配置静态IP为10.110.10.2/24;
  3. 将PC3连接到交换机2的Ethernet 0/0/13上,配置静态IP为10.110.10.3/24;
  4. 将PC4连接到交换机2的Ethernet 0/0/20上,配置静态IP为10.110.10.4/24;
  5. 找根网线把交换机1的Ethernet 0/0/23和交换机2的Ethernet 0/0/23连接起来。

大概会是这个样子:

VLAN_LAB_17.png

接下来继续回到Xshell上开始配置交换机,我们先来折腾交换机1:

  1. 将Ethernet 0/0/3添加到VLAN 3中;
  2. 将Ethernet 0/0/12添加到VLAN 2中;
  3. 将Ethernet 0/0/23配置为trunk并允许VLAN 2/3的流量通过。

这台交换机的端口就不像想象中的那样了:

VLAN_LAB_18.png
端口和另一台不大一样

这里是GigabitEthernet0/0/1

这里添加各端口到VLAN中的命令大家应该都清楚了,不再挂图了…看看trunk:

VLAN_LAB_19.png

别忘了还要允许流量通过:

VLAN_LAB_20.png

是不是又想说和PPT里不一样?可是官方文档里可是清清楚楚地只有上面图里那种用法的哦。自己动手,丰衣足食吧。

VLAN_LAB_21.png

检查过没有问题了就可以继续配置交换机2了:

  1. 将Ethernet0/0/13添加到VLAN 3中;
  2. 将Ethernet0/0/20添加到VLAN 2中;
  3. 将Ethernet0/0/23配置为trunk并允许VLAN 2/3的流量通过。

跟上面差不多,我就不累死累活上图了。如果两台都配置好了之后就可以来测试了!根据实验要求:

PC1和PC2之间能否ping成功:

VLAN_LAB_22.png

由于PC1和PC2不在同一个VLAN,并且不存在可达路由,所以不能ping通。

PC1和PC3之间能否ping成功:

VLAN_LAB_23.png

PC1和PC3在同一个VLAN中,并且配置好了trunk,可以ping通。

PC2和PC4之间能否ping成功:

VLAN_LAB_24.png

同一VLAN,并且有正确的trunk,可以ping通。

附加一个PC2和PC3:

VLAN_LAB_25.png

大概这样子就圆满完成了。不过走之前别忘了删掉VLAN,网线插回原位之类的扫尾工作哈。

控制台记录

最后附一些控制台记录,仅供参考,并且有我瞎玩的记录。请务必记得看文档。

<Quidway>system-view 
[Quidway]interface Vlanif 2
Error: The VLAN does not exist.
[Quidway]vlan 2 
[Quidway-vlan2]quit
[Quidway]interface Vlanif 2
[Quidway-Vlanif2]ip address 10.110.10.3 255.255.255.0
[Quidway]undo vlan 2
Error: The VLAN has a L3 interface. Please delete it first.
[Quidway]vlan 2 
[Quidway-vlan2]port Ethernet0/0/3
Error: Trunk or Hybrid port(s) cannot be added or deleted in this manner.
[Quidway]interface Ethernet0/0/3
[Quidway-Ethernet0/0/3]port link-type access
[Quidway-Ethernet0/0/3]port default vlan 2
[Quidway-Ethernet0/0/3]display vlan 2
* : management-vlan
---------------------
VLAN ID Type         Status   MAC Learning Broadcast/Multicast/Unicast Property 
--------------------------------------------------------------------------------
2       common       enable   enable       forward   forward   forward default  
----------------
Untagged   Port: Ethernet0/0/3               
----------------
Interface                   Physical 
Ethernet0/0/3               UP      
[Quidway]interface GigabitEthernet0/0/23
[Quidway-GigabitEthernet0/0/23]port link-type trunk
[Quidway-GigabitEthernet0/0/23]port trunk allow-pass vlan all

Chrome 离线安装

推荐同学用Google Chrome作为日常的浏览器,但是他表示很难下载成功。为了省事,我决定直接弄个离线安装包给他。

直接搜索chrome offline installer,找到了官方文档:下載及安裝 Google Chrome – 電腦 – Google Chrome說明。文中说明了在离线状态下安装Chrome,需要在联网的主机上下载备用Chrome安装程序,就是我想要的离线安装包了。

只要打开上述链接再点击下载Chrome就可以得到离线安装包了。

仔细看了一下那个链接,其实本质上只是在Chrome的官方网址https://www.google.com/chrome/后面加上?standalone=1而已。即:https://www.google.com/chrome/?standalone=1

访问试一下,没有问题,有图有真相:

IDM抓取到的,看文件名和大小应该没有问题。

类似问题:Google Chrome 离线安装包的官方下载地址是什么? – 知乎

Public DNS 个人常用的公共DNS

慢慢收藏一点方便自己查找。

DNSPod Public DNS 119.29.29.29

首选:119.29.29.29
备用:182.254.116.116

Public DNS+——DNSPod推出的域名递归解析服务
Public DNS+ 接入指南-DNSPod 技术支持-DNSPod-免费智能DNS解析服务商-电信_网通_教育网,智能DNS

阿里DNS

首选:223.5.5.5
备用:223.6.6.6
首选:2400:3200::1
备用:2400:3200:baba::1

阿里DNS

1.1.1.1

首选:1.1.1.1
备用:1.0.0.1
首选:2606:4700:4700::1111
备用:2606:4700:4700::1001

1.1.1.1 — the Internet’s Fastest, Privacy-First DNS Resolver

Google Public DNS

首选:8.8.8.8
备用:8.8.4.4
首选:2001:4860:4860::8888
备用:2001:4860:4860::8844

Get Started | Public DNS | Google Developers

Foobar2000 简单上手

Foobar2000是一个强大的音乐播放器。好久没有拿出来用了,今天简单记录一下。

官网:foobar2000

Monkey’s Audio files (.APE)

下载安装就不用说了。印象中默认是不能直接播放ape格式的文件的,需要安装解码器(decoder)。可以在官网找到:foobar2000: Components Repository – Monkey’s Audio Decoder。正常情况下下载后直接双击这个插件就可以安装了。

双击后应该会弹出插件列表

双击后应该会弹出插件列表,点击Apply即可。

如果没有弹出插件列表,可能是插件的文件格式没有被关联上,这个时候只需要到插件列表里手动安装即可:

进入设置,设置里的第一个(Components)就是了
点击Install,并找到你刚下载好的插件就可以了

安装会要求重启Foobar2000,重启后就可以播放ape格式的文件了。

Free Encoder Pack

想要转换格式的时候发现,如果想要转换成flac等格式的文件,会发现缺少编码器,要求你提供flac.exe的位置。

同样地,要解决这个问题,可以在官网上找到一个整合了很多编码器的包:foobar2000: Free Encoder Pack,描述称:这个包内含多个用于转换的编码器文件。下载安装即可。

安装后再执行转换就不会要求你选择编码器了。

音乐库扫描内含*.cue的文件

默认音乐库只扫描个人文件夹下的音乐文件夹,可以在设置中自行设置。打开设置(Preferences,偏好项),点击媒体库(Media Library)即可自行修改目录。

Preferences->Media Library

注意到了Exclude项,它默认排除识别*.cue文件,这通常不是我们想要的,因为这样会使得那些完全依赖cue文件进行分轨的整轨音乐无法被识别为多个音乐文件。在某些时候可以考虑删除它,此时应该能正常识别了。

此外,Shell Integration中也默认排除*.cue文件,自行根据需要调整吧。

要指出的是,某些安全软件会阻止你修改这些选项,有点迷…

大概就先这样吧,想到什么再补。

集合(运算符重载)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

集合是由一个或多个确定的元素所构成的整体。集合的运算有并、交、相对补等。
集合A和集合B的交集:由属于A且属于B的相同元素组成的集合。
集合A和集合B的并集:由所有属于集合A或属于集合B的元素所组成的集合。
集合B关于集合A的相对补集,记做A-B:由属于A而不属于B的元素组成的集合。
假设集合A={10,20,30},集合B={1,10,50,8}。则A与B的并是{10,20,30,1,50,8},A与B的交是{10},B关于A的相对补集是{20,30}。
定义整数集合类CSet,属性包括:集合中的元素个数n,整型指针data存储集合中的元素。
方法有:重载输出,按样例格式输出集合中的元素。
重载+运算符,求集合A和集合B的并集,并返回结果集合。
重载-运算符,求集合B关于集合A的相对补集,并返回结果集合。
重载*运算符,求集合A和集合B的交集,并返回结果集合。
主函数输入集合A、B的数据,计算集合的并、交、相对补。
可根据题目,为CSet类添加需要的成员函数。

输入

测试次数
每组测试数据两行,格式如下:
第一行:集合A的元素个数和元素
第二行:集合B的元素个数和元素

输出

每组测试数据输出如下:
第一行:集合A
第二行:集合B
第三行:A和B的并
第四行:A和B的交
第五行:B关于A的相对补集 与 A关于B的相对补集的并,即(A-B)+(B-A)
每组测试数据间以空行分隔。

样例输入

2
3 10 20 30
4 10 1 2 3
5 100 2 3 4 -10
6 -34 12 2 4 90 100

样例输出

A:10 20 30
B:10 1 2 3
A+B:10 20 30 1 2 3
A*B:10
(A-B)+(B-A):20 30 1 2 3

A:100 2 3 4 -10
B:-34 12 2 4 90 100
A+B:100 2 3 4 -10 -34 12 90
A*B:100 2 4
(A-B)+(B-A):3 -10 -34 12 90

提示

解决方案

这个其实没啥好说的…思路清晰就可以了。并集运算可以先生成一个临时数组,加入集合A的元素,然后再加入集合B中不和已加入元素重复的元素。相对补集可以先生成、添加,然后再删除重复的。

主要问题可能在于有同学试图返回一个指向了即将失效的临时变量的指针。

You are returning a temporary object, but because you return it by value, the copy is created. If you return pointer or reference to temporary object, that would be a mistake.
If you change the return type to const char * and return ss.str().c_str() you would return pointer to some buffer of temporary std::string returned by ss.str() and that would be bad.

C++ returning temporary objects confusion – Stack Overflow
#include <iostream>
#include <vector>
#include <algorithm>

class Set {
public:
    explicit Set(std::vector<int> data) : data(std::move(data)) {}

    Set operator+(const Set &rhs) const {
        std::vector<int> tmp;
        tmp.assign(this->data.begin(), this->data.end());
        for (auto i : rhs.data) {
            if (std::find(tmp.begin(), tmp.end(), i) == tmp.end()) {
                tmp.push_back(i);
            }
        }
        return Set(tmp);
    }

    Set operator-(const Set &rhs) const {
        std::vector<int> tmp;
        tmp.assign(this->data.begin(), this->data.end());
        for (auto i : rhs.data) {
            auto it = std::find(tmp.begin(), tmp.end(), i);
            if (it != tmp.end()) {
                tmp.erase(it);
            }
        }
        return Set(tmp);
    }

    Set operator*(const Set &rhs) const {
        std::vector<int> tmp;
        for (auto i1 : this->data) {
            for (auto i2 : rhs.data) {
                if (i1 == i2) {
                    tmp.push_back(i1);
                }
            }
        }
        return Set(tmp);
    }

    friend std::ostream &operator<<(std::ostream &os, const Set &rhs) {
        std::cout << rhs.data.front();
        for (int i = 1; i < rhs.data.size(); ++i) {
            os << ' ' << rhs.data[i];
        }
        return os;
    }

private:
    std::vector<int> data;
};

int main() {
    int T;
    std::cin >> T;

    while (T--) {
        int sizeA, sizeB;
        std::cin >> sizeA;
        std::vector<int> vectorA(sizeA);
        for (auto &i : vectorA) {
            std::cin >> i;
        }
        std::cin >> sizeB;
        std::vector<int> vectorB(sizeB);
        for (auto &i : vectorB) {
            std::cin >> i;
        }
        Set setA(vectorA), setB(vectorB);
        std::cout << "A:" << setA << std::endl
                  << "B:" << setB << std::endl
                  << "A+B:" << setA + setB << std::endl
                  << "A*B:" << setA * setB << std::endl
                  << "(A-B)+(B-A):" << (setA - setB) + (setB - setA) << std::endl;
        std::cout << std::endl;
    }

    return 0;
}

母牛生小牛问题(静态数据成员与静态成员函数)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

假设母牛从出生起第4个年头开始每年可以生一头小母牛,但是第11年后死亡。按此规律,第n年时有多少头母牛?(假设n不大于30)
定义一个母牛类CCow,能够用动态创建和撤消类对象的方式来模拟小母牛的出生和死亡规律。试编写C++程序完成上述计算。

输入

第一行输入测试次数
每次测试输入一行,表示第几年的整数n(<=30)

输出

每次测试输出一行,第n年的母牛总数

样例输入

3
7
30
25

样例输出

6
28364
4530

提示

解决方案

额,写完才看到标题…懒得改了,事实上只要去掉Solver类再稍加改动就可以符合要求。
这里主要思路就是相当于有一个上帝视角:std::vector<Cow *> cows,看着所有的牛。每次循环意味着过去一年,于是让所有牛成长、出生或是死亡。
更新:突然发现还忘记delete,别锤我…
另外每次改动容器时某些情况会让迭代器失效,太久没看《Effective STL》了,有空找来这里补上一点东西。

#include <iostream>
#include <vector>

class CowSolver {
public:
    explicit CowSolver(int limit) : limit(limit) {
        cows.push_back(new Cow());
        for (int i = 1; i < limit; ++i) {
            growUp();
            create();
            kill();
        }
    }

    void println() {
        std::cout << cows.size() << std::endl;
    }

private:
    struct Cow {
        Cow() : age(1) {}

        int age;
    };

    int limit;
    std::vector<Cow *> cows;

    void growUp() {
        for (auto item : cows) {
            item->age += 1;
        }
    }

    void create() {
        for (int i = 0; i < cows.size(); ++i) {
            if (4 <= cows[i]->age && cows[i]->age < 11) {
                cows.push_back(new Cow());
            }
        }
    }

    void kill() {
        for (auto it = cows.begin(); it != cows.end();) {
            if ((*it)->age == 11) {
                it = cows.erase(it);
            } else {
                ++it;
            }
        }
    }
};

int main() {
    int T;
    std::cin >> T;

    while (T--) {
        int year;
        std::cin >> year;

        CowSolver cowSolver(year);
        cowSolver.println();
    }

    return 0;
}

扑克牌排序(结构体)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

自定义结构体表示一张扑克牌,包含类型——黑桃、红桃、梅花、方块、王;大小——2,3,4,5,6,7,8,9,10,J,Q,K,A,小王(用0表示)、大王(用1表示)。输入n,输入n张扑克牌信息,从大到小输出它们的排序结果。
假设扑克牌的排序规则是大王、小王为第一大、第二大,剩余52张扑克牌按照先花色后大小排序。
花色:黑桃>红桃>梅花>方块。
大小: A>K>Q>J>>10>9>…>2。
提示:百度sort函数、strstr函数使用。

输入

测试次数t
每组测试数据两行:
第一行:n,表示输入n张扑克牌
第二行:n张扑克牌信息,格式见样例

输出

对每组测试数据,输出从大到小的排序结果

样例输入

3
5
黑桃4 红桃10 梅花Q 方块K 黑桃A
10
大王 梅花10 红桃K 方块9 黑桃2 梅花A 方块Q 小王 黑桃8 黑桃J
5
红桃K 梅花K 黑桃K 方块K 小王

样例输出

黑桃A 黑桃4 红桃10 梅花Q 方块K
大王 小王 黑桃J 黑桃8 黑桃2 红桃K 梅花A 梅花10 方块Q 方块9
小王 黑桃K 红桃K 梅花K 方块K

提示

解决方案

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <assert.h>

class Poker {
public:
    friend std::istream &operator>>(std::istream &in, Poker &poker) {
        in >> poker.info;
        return in;
    }

    friend std::ostream &operator<<(std::ostream &out, const Poker &poker) {
        out << poker.info;
        return out;
    }

    bool operator<(const Poker &rhs) const {
        if (this->typeValue() != rhs.typeValue()) {
            return this->typeValue() < rhs.typeValue();
        } else {
            return this->sizeValue() < rhs.sizeValue();
        }
    }

private:
    std::string info;

    int typeValue() const {
        std::string type;
        if (info.find("大王") != std::string::npos) {
            return 6;
        } else if (info.find("小王") != std::string::npos) {
            return 5;
        } else if (info.find("黑桃") != std::string::npos) {
            return 4;
        } else if (info.find("红桃") != std::string::npos) {
            return 3;
        } else if (info.find("梅花") != std::string::npos) {
            return 2;
        } else if (info.find("方块") != std::string::npos) {
            return 1;
        }
        assert(0);
    }

    int sizeValue() const {
        char back = info[info.length() - 1];
        switch (back) {
            default:
                return back - '0';
            case '0':
                return 10;
            case 'J':
                return 11;
            case 'Q':
                return 12;
            case 'K':
                return 13;
            case 'A':
                return 14;
        }
    }
};

int main() {
    size_t T;
    std::cin >> T;

    while (T--) {
        size_t size;
        std::cin >> size;
        std::vector<Poker> vector(size);
        for (size_t i = 0; i < vector.size(); ++i) {
            std::cin >> vector[i];
        }

        std::sort(vector.rbegin(), vector.rend());

        std::cout << vector.front();
        for (size_t i = 1; i < vector.size(); ++i) {
            std::cout << ' ' << vector[i];
        }
        std::cout << std::endl;
    }

    return 0;
}

引用:
Check if a string contains a string in C++ – Stack Overflow
c++ – Does Overloading Operator<< works inside the class? – Stack Overflow
sort – C++ Reference

矩阵类模板(类模板)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

设计一个矩阵类模板Matrix,支持任意数据类型的数据。
要求至少包含2个成员函数:矩阵转置函数transport、以及打印输出函数print
编写main函数进行测试,调用类的成员函数完成转置和输出。

输入

第一行先输入t,表示有t个测试用例
从第二行开始输入每个测试用例的数据。
首先输入数据类型,I表示int,D表示double,C表示char,接着输入两个参数m和n,分别表示矩阵的行和列
接下来输入矩阵的元素,一共m行,每行n个数据

输出

输出转置后的矩阵

样例输入

2
I 2 3
1 2 3
4 5 6
C 3 3
a b c
d e f
g h i

样例输出

1 4
2 5
3 6
a d g
b e h
c f i

提示

解决方案

冷静思考就能解决。

看到有同学重载[]运算符时返回了一个指向了T的指针(即T *)。这样的话,访问元素时,第二个[]就是在索引数组了。虽然可以做到是对的,但是我总觉得很别扭,大概是我觉得不应该直接去操作class中private的成员吧…个人觉得还好的方案就是下面这种返回结构体的,或是封装一下std::vector<std::vector<T> >也都还好。

#include <iostream>

template<typename T>
class Matrix {
public:
    Matrix(size_t row, size_t col) : row(row), col(col) {
        data = new T *[row];
        for (size_t i = 0; i < row; ++i) {
            data[i] = new T[col];
        }
    }

    void setFromCin() {
        for (size_t ir = 0; ir < row; ++ir) {
            for (size_t ic = 0; ic < col; ++ic) {
                std::cin >> data[ir][ic];
            }
        }
    }

    void transport() {
        T **tmp = new T *[col];
        for (size_t ir = 0; ir < col; ++ir) {
            tmp[ir] = new T[row];
        }
        for (size_t ir = 0; ir < col; ++ir) {
            for (size_t ic = 0; ic < row; ++ic) {
                tmp[ir][ic] = data[ic][ir];
            }
        }

        for (size_t ir = 0; ir < row; ++ir) {
            delete[](data[ir]);
        }
        delete[](data);
        std::swap(row, col);
        data = tmp;
    }

    void println() {
        for (size_t ir = 0; ir < row; ++ir) {
            std::cout << data[ir][0];
            for (size_t ic = 1; ic < col; ++ic) {
                std::cout << ' ' << data[ir][ic];
            }
            std::cout << std::endl;
        }
    }

    struct Row &operator[](int index) {
        return Row(data[index]);
    }

    ~Matrix() {
        for (size_t ir = 0; ir < row; ++ir) {
            delete[](data[ir]);
        }
        delete[](data);
    }

private:
    int row, col;
    T **data;

    struct Row {
        explicit Row(T *row) : data(row) {}

        T operator[](int index) {
            return data[index];
        }

        T *data;
    };
};

int main() {
    int T;
    std::cin >> T;

    while (T--) {
        char type;
        std::cin >> type;
        int row, col;
        std::cin >> row >> col;
        switch (type) {
            case 'I': {
                Matrix<int> matrix(row, col);
                matrix.setFromCin();
                matrix.transport();
                matrix.println();
                break;
            }
            case 'D': {
                Matrix<double> matrix(row, col);
                matrix.setFromCin();
                matrix.transport();
                matrix.println();
                break;
            }
            case 'C': {
                Matrix<char> matrix(row, col);
                matrix.setFromCin();
                matrix.transport();
                matrix.println();
                break;
            }
            default: ;
        }
    }

    return 0;
}