2017中国大学生程序设计竞赛-杭州站 Problem C. Hakase and Nano(博弈)

本文探讨了一种涉及石子堆的游戏策略,其中两名玩家轮流从成对的石子堆中取石子,目标是在对手无法行动时取得最后一颗石子。通过分析不同开局条件,如石子堆数量和每堆石子的数量,我们确定了确保玩家Hakase胜利的条件,特别是当考虑到其每回合可取两次的优势时。

题意分析:

有n堆石子,每次可以取一对石子的一颗或多颗,取得最后一颗石子的人获胜,Hakase每回合可以取两次,Nano每回合可以取一次,给出Hakase先手或后手,求Hakase会不会获胜。

解题思路:

因为Hakase每回合可以取两次,大部分情况下可以获胜,所以应该考虑Hakase失败的情况,当先手时,如果堆数是3的倍数,且全为1,那么他必败,因为每次H必须取两堆,对方取一堆,最后一堆刚好是对方取。

还有是对方先取,比如1, 1, 3,可以把第三堆取2个,剩下1, 1, 1,这种情况也是必败,

再比如1, 1, 1, 3, 可以直接把最后一堆3取完,剩下1, 1, 1,也是必败。

也就是刚好有一堆可以让对方取使剩下的堆数是3的倍数,且全为1的1情况是必败的。

#include <stdio.h>
int main()
{
    int t, n, temp, a, sum1, sum2;
    scanf("%d", &t);
    while(t--)
    {
        sum1=0; sum2=0;
        scanf("%d%d", &n, &temp);
        for(int i=1; i<=n; i++)
        {
            scanf("%d", &a);
            if(a==1)
                sum1++;
            else
                sum2++;
        }
        if(temp==1) {
            if(sum2==0 && sum1%3==0)
                printf("No\n");
            else
                printf("Yes\n"); 
        }
        else {
            if(n==1) 
                printf("No\n");
            else if(n==2)
                printf("Yes\n");
            else if(sum2==0 || sum2==1)    {
                if(sum2==0 && sum1%3==1)
                    printf("No\n");
                else if(sum2==1 && ((sum1+1)%3==0 || sum1%3==0))
                    printf("No\n");
                else printf("Yes\n");
            }
            else printf("Yes\n");
        }
    }
    return 0;
}

 

<think>好的,我现在需要帮助用户找到基于QR分解实现数字水印嵌入的Matlab代码示例。用户提供了两个图片路径,水印文件在C:\\Users\\11696\\Pictures\\图片素材\\animal_chara_mogura_hakase.png,目标图片是C:\\Users\\11696\\Desktop\\fzj.jpg。我需要先理解QR分解在水印中的应用原理,然后结合Matlab代码实现。 首先,我得回忆一下QR分解的基本概念。QR分解是将矩阵分解为正交矩阵Q和上三角矩阵R的过程。在图像处理中,通常会对图像的矩阵进行分解,然后通过修改其中的某些系数来嵌入水印,因为Q矩阵的正交性可能对扰动更鲁棒,这样水印的嵌入可能更隐蔽。 接下来,水印嵌入的一般步骤可能包括:读取宿主图像和水印图像,将宿主图像分块,对每个块进行QR分解,然后将水印信息嵌入到R矩阵的特定位置,最后通过逆运算重建图像。但具体如何操作呢?可能需要参考一些文献或已有的方法。 用户提供的引用中,引用[1]是关于QR分解的Matlab代码,而引用[2]涉及QR二维码的图像处理。虽然用户的问题是关于数字水印,但可能可以借鉴QR分解在图像处理中的应用方法。比如,在QR分解后的矩阵中嵌入水印信息,比如修改R矩阵的一些系数,或者利用Q矩阵的某些特性。 我需要考虑宿主图像的处理方式。通常,数字水印需要嵌入到宿主图像的变换域中,比如DCT、DWT或者QR分解后的矩阵。这里,QR分解可能被用来分解图像块,然后修改R矩阵的元素来嵌入水印。例如,将水印图像的像素值经过处理后嵌入到R矩阵的某些位置,这样在提取时可以通过QR分解来恢复水印。 另外,水印可能需要是二值图像,方便嵌入和提取。用户提供的水印文件是PNG格式,可能需要先转换为灰度或二值图像。宿主图像是JPG格式,同样需要读取为矩阵进行处理。 在Matlab中,读取图像使用imread函数,处理时需要转换为double类型以便进行矩阵运算。然后,可能需要对宿主图像分块,每个块进行QR分解,嵌入水印信息到R矩阵中,再通过Q和修改后的R重建块,最后合并所有块得到含水印的图像。 需要注意的是,嵌入水印的强度参数,即alpha值,用来控制水印的可见性和鲁棒性之间的平衡。较大的alpha值可能导致水印更明显,但可能影响宿主图像质量;较小的值可能更隐蔽,但提取时可能困难。 在代码结构上,大致步骤如下: 1. 读取宿主图像和水印图像,转换为灰度。 2. 调整水印图像尺寸以匹配宿主图像。 3. 将宿主图像分块,例如8x8的块。 4. 对每个块进行QR分解。 5. 修改R矩阵的特定元素(比如某个位置)来嵌入水印信息。 6. 通过Q*R_new重建块,合并所有块得到含水印的图像。 7. 保存结果并显示图像。 在嵌入水印时,可能需要将水印图像二值化,每个像素对应一个嵌入位。例如,水印像素为1时,增加R矩阵某个元素的值;为0时则减少。提取时,通过比较该元素的值是否大于原始值来判断水印位。 现在,我需要考虑代码的具体实现。例如,宿主图像的分块处理,可能使用循环遍历每个块,然后进行QR分解。但宿主图像的尺寸可能需要被分块大小整除,否则需要调整或填充。此外,水印图像可能需要调整到与宿主图像分块数量匹配的大小,比如每个块对应一个水印像素。 例如,宿主图像大小为MxN,分块大小为8x8,则分块数量为(M/8)*(N/8)。水印图像需要调整为这个数量对应的尺寸,或者截取/重复以匹配。如果水印图像较小,可能需要重复;如果较大,可能需要缩放。 嵌入的位置可以选择R矩阵的某个固定位置,例如R(1,1)或R(2,2),但需要注意修改后的值不应破坏R的上三角结构,所以只能修改对角线或上三角部分的元素。可能选择对角线元素,因为它们对重构的影响较大,鲁棒性更好。 在代码中,宿主图像和水印的读取: host_img = imread('C:\\Users\\11696\\Desktop\\fzj.jpg'); watermark = imread('C:\\Users\\11696\\Pictures\\图片素材\\animal_chara_mogura_hakase.png'); 然后转换为灰度: host_gray = rgb2gray(host_img); watermark_gray = rgb2gray(watermark); 调整水印尺寸到宿主的分块数量: 假设分块大小为block_size=8,宿主图像尺寸为[rows, cols],则分块数量为num_blocks_row = rows/block_size,num_blocks_col = cols/block_size。水印需要调整到num_blocks_row x num_blocks_col的大小。例如: [rows, cols] = size(host_gray); block_size = 8; num_blocks_row = floor(rows / block_size); num_blocks_col = floor(cols / block_size); watermark_resized = imresize(watermark_gray, [num_blocks_row, num_blocks_col]); 然后将水印二值化,例如使用阈值处理: watermark_binary = imbinarize(watermark_resized); 接下来,对宿主图像分块,每个块进行QR分解,嵌入水印: watermarked_img = zeros(size(host_gray)); for i = 1:num_blocks_row for j = 1:num_blocks_col % 获取当前块 row_start = (i-1)*block_size + 1; row_end = i*block_size; col_start = (j-1)*block_size + 1; col_end = j*block_size; block = host_gray(row_start:row_end, col_start:col_end); % QR分解 [Q, R] = qr(double(block)); % 嵌入水印,例如修改R(1,1) alpha = 0.1; % 强度系数 if watermark_binary(i,j) == 1 R(1,1) = R(1,1) + alpha * abs(R(1,1)); else R(1,1) = R(1,1) - alpha * abs(R(1,1)); end % 重建块 reconstructed_block = Q * R; % 确保数值在0-255之间 reconstructed_block = max(min(reconstructed_block, 255), 0); % 放回水印图像 watermarked_img(row_start:row_end, col_start:col_end) = reconstructed_block; end end 然后显示并保存: figure; subplot(1,2,1); imshow(host_gray, []); title('原图'); subplot(1,2,2); imshow(uint8(watermarked_img), []); title('含水印图像'); imwrite(uint8(watermarked_img), 'watermarked_image.jpg'); 但需要考虑数据类型转换,宿主图像可能为uint8,而QR分解需要转换为double。另外,重建后的块需要转换回uint8。此外,水印的嵌入位置和提取方法需要对应。比如在提取时,同样分块并进行QR分解,比较R矩阵的特定元素是否被修改过。 不过用户可能需要的是完整的代码示例,所以需要将上述思路整理为正确的Matlab代码,并处理可能的错误,比如图像路径是否正确,分块是否整除,调整水印尺寸的方式等。此外,可能需要添加鲁棒性处理,例如对宿主图像进行分块时,如果尺寸不能整除,如何处理边缘部分。可能采用忽略边缘或者补零的方式。 综上,我需要提供一个完整的Matlab代码,实现基于QR分解的数字水印嵌入,并给出解释。同时,用户可能需要知道如何提取水印,但当前问题可能只涉及嵌入部分。但根据用户的问题,主要是生成嵌入的代码,所以可能不需要提取部分。不过用户可能需要完整的支持,所以代码可能需要包括嵌入和提取的示例?或者用户只需要嵌入? 根据用户的问题,用户需要的是“基于QR分解实现数字水印嵌入的Matlab代码示例”,所以提供嵌入的代码即可,但可能提取部分也需要简要说明,或者作为相关问题的回答。 现在,我需要验证代码的正确性。比如,是否在嵌入水印后,图像质量变化不大,并且能够提取出水印。可能需要调整alpha的值,使得水印不可见但可提取。 可能的错误点包括: 1. 分块索引超出宿主图像的范围。例如,当宿主图像的行数或列数不是block_size的整数倍时,最后一个块可能超出范围。解决方案是将宿主图像裁剪到合适的尺寸,或者在分块时处理边缘部分。 在代码中,可以用: rows = num_blocks_row * block_size; cols = num_blocks_col * block_size; host_gray = host_gray(1:rows, 1:cols); 这样调整宿主图像的大小,确保分块时不会越界。 2. 水印二值化处理是否正确,可能需要根据水印图像的具体情况调整阈值。 3. 在修改R矩阵的元素时,是否需要考虑正负号?比如,原R(1,1)可能为正,但修改时如果水印位为0,可能减少,但需要确保修改后的R矩阵在重建时不会导致块出现负值或过大值。因此,在重建后需要限制数值范围,使用max和min函数。 4. 水印嵌入的位置选择是否合理,比如选择R(1,1)可能对图像的影响较大,但可能更容易被攻击破坏。但作为示例,可能已经足够。 综上,编写代码如下: 首先调整宿主图像和水印的尺寸,分块处理,嵌入水印到R(1,1),然后重建。提取水印时,同样分块,进行QR分解,根据R(1,1)的值判断水印位。 现在,将以上思路转化为具体的Matlab代码,并添加注释。同时,处理可能的路径问题,比如反斜杠需要转义,或者使用正斜杠。在Matlab中,路径可以使用正斜杠或双反斜杠。 最终代码的大致结构应该包括: 读取图像,调整尺寸,分块,嵌入水印,保存结果。 现在,我需要将上述思路整合成一个完整的代码,并确保语法正确。可能还需要进行测试,但由于无法实际运行,只能逻辑上检查。例如: % 读取宿主图像和水印图像 host_path = 'C:\Users\11696\Desktop\fzj.jpg'; watermark_path = 'C:\Users\11696\Pictures\图片素材\animal_chara_mogura_hakase.png'; host_img = imread(host_path); watermark_img = imread(watermark_path); % 转换为灰度图像 host_gray = rgb2gray(host_img); watermark_gray = rgb2gray(watermark_img); % 设置分块大小 block_size = 8; [rows, cols] = size(host_gray); % 调整宿主图像尺寸为block_size的整数倍 num_blocks_row = floor(rows / block_size); num_blocks_col = floor(cols / block_size); rows_new = num_blocks_row * block_size; cols_new = num_blocks_col * block_size; host_gray = host_gray(1:rows_new, 1:cols_new); % 调整水印图像尺寸为分块数量 watermark_resized = imresize(watermark_gray, [num_blocks_row, num_blocks_col]); % 二值化水印 watermark_binary = imbinarize(watermark_resized); % 初始化含水印图像 watermarked_img = double(host_gray); % 嵌入水印 alpha = 0.05; % 调整此参数控制水印强度 for i = 1:num_blocks_row for j = 1:num_blocks_col % 获取当前块 row_start = (i-1)*block_size + 1; row_end = i*block_size; col_start = (j-1)*block_size + 1; col_end = j*block_size; block = double(host_gray(row_start:row_end, col_start:col_end)); % QR分解 [Q, R] = qr(block); % 嵌入水印到R(1,1) if watermark_binary(i,j) R(1,1) = R(1,1) + alpha * abs(R(1,1)); else R(1,1) = R(1,1) - alpha * abs(R(1,1)); end % 重建块 reconstructed_block = Q * R; % 替换到含水印图像中 watermarked_img(row_start:row_end, col_start:col_end) = reconstructed_block; end end % 转换为uint8并保存 watermarked_img = uint8(max(min(watermarked_img, 255), 0)); imwrite(watermarked_img, 'watermarked_fzj.jpg'); % 显示图像 figure; subplot(1,3,1); imshow(host_gray, []); title('原图'); subplot(1,3,2); imshow(watermarked_img, []); title('含水印图像'); subplot(1,3,3); imshow(watermark_binary, []); title('水印图像'); 需要注意的是,宿主图像可能原本是彩色图像,但代码中转换为灰度,如果用户希望处理彩色图像,可能需要分别对每个通道进行处理,或者转换到其他颜色空间。但用户可能希望简单处理,所以使用灰度。 另外,水印的调整方式是否正确,比如使用imresize时可能采用默认的双三次插值,但二值水印可能需要最近邻插值来保持二值性。因此,在调整水印尺寸时,可能需要指定方法: watermark_resized = imresize(watermark_gray, [num_blocks_row, num_blocks_col], 'nearest'); 然后再二值化,或者直接二值化后再调整尺寸: watermark_binary = imbinarize(watermark_gray); watermark_resized = imresize(watermark_binary, [num_blocks_row, num_blocks_col], 'nearest'); 这可能更准确,但需要看水印图像的具体情况。 综上,可能需要对水印处理部分进行调整,确保二值化正确。但作为示例,上述代码已经足够。 现在,我需要检查代码中的路径是否正确,特别是反斜杠需要转义为双反斜杠。在Matlab中,路径中的反斜杠需要写成双反斜杠,或者使用正斜杠。例如: host_path = 'C:\\Users\\11696\\Desktop\\fzj.jpg'; watermark_path = 'C:\\Users\\11696\\Pictures\\图片素材\\animal_chara_mogura_hakase.png'; 或者使用正斜杠: host_path = 'C:/Users/11696/Desktop/fzj.jpg'; watermark_path = 'C:/Users/11696/Pictures/图片素材/animal_chara_mogura_hakase.png'; 两种方式在Matlab中都是可行的。 另外,宿主图像可能原本就是灰度,不需要转换,但为了保险起见,代码中使用rgb2gray处理。 最后,生成的相关问题可能涉及水印的提取方法、参数调整对鲁棒性的影响、不同分块大小的比较等。</think>以下是一个基于QR分解实现数字水印嵌入的Matlab代码示例,包含完整路径支持和分块处理逻辑: ```matlab %% 图像水印嵌入主程序 % 输入图像路径 host_path = 'C:\\Users\\11696\\Desktop\\fzj.jpg'; watermark_path = 'C:\\Users\\11696\\Pictures\\图片素材\\animal_chara_mogura_hakase.png'; % 参数设置 block_size = 8; % 分块大小 alpha = 0.1; % 水印嵌入强度系数 %% 步骤1:图像预处理 % 读取宿主图像并转为灰度 host_img = imread(host_path); if size(host_img,3) == 3 host_gray = rgb2gray(host_img); else host_gray = host_img; end host_gray = double(host_gray); % 读取水印图像并预处理 watermark = imread(watermark_path); watermark_gray = rgb2gray(watermark); watermark_binary = imbinarize(imresize(watermark_gray,[size(host_gray,1),size(host_gray,2)],'nearest')); %% 步骤2:分块QR分解 [rows,cols] = size(host_gray); watermarked = zeros(size(host_gray)); for i = 1:block_size:rows-block_size+1 for j = 1:block_size:cols-block_size+1 % 提取图像块 block = host_gray(i:i+block_size-1,j:j+block_size-1); % QR分解(使用经济型分解) [Q,R] = qr(block,0); % 水印嵌入规则:修改R(1,1)系数 R(1,1) = R(1,1) + alpha * watermark_binary(i,j) * abs(R(1,1)); % 重构含水印块 watermarked(i:i+block_size-1,j:j+block_size-1) = Q * R; end end %% 步骤3:后处理与输出 % 数据格式转换与保存 watermarked = uint8(max(min(watermarked,255),0)); imwrite(watermarked,'watermarked_image.jpg'); % 显示对比结果 figure; subplot(1,3,1), imshow(host_gray,[]), title('原始图像'); subplot(1,3,2), imshow(watermarked,[]), title('含水印图像'); subplot(1,3,3), imshow(watermark_binary), title('二值水印'); ``` ### 实现原理说明 1. **QR分解特性**:通过正交矩阵$Q$和上三角矩阵$R$的分解,将水印信息嵌入到$R$矩阵的对角线元素中。由于$R$矩阵的能量集中特性,这种嵌入方式具有较好的鲁棒性[^1]。 2. **分块处理**:将图像划分为$8\times8$的块进行局部特征处理,这种分块方式平衡了计算复杂度和特征保持需求[^2]。 3. **水印嵌入**:通过修改每个分块的$R(1,1)$元素值,将二值水印信息编码到频域系数中。其中$\alpha$参数控制嵌入强度,需在不可见性和鲁棒性之间取得平衡。 ### 注意事项 - 路径中的反斜杠需转义为双反斜杠`\\` - 建议$\alpha$取值在0.05-0.2之间进行实验 - 水印提取需采用逆向QR分解过程 - 对JPEG压缩等攻击具有有限鲁棒性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张宜强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值