UVA.11464 Even Parity (思维题 开关问题)

本文解析了一道经典的算法题目——UVA.11464 EvenParity,该题要求在给定的n*n矩阵中通过最少次数的0到1的转换,使得每个格子的相邻格子数值之和为偶数。文章详细介绍了如何通过二进制枚举和动态推导的方法高效解决此问题,并提供了完整的代码实现。

UVA.11464 Even Parity (思维题 开关问题)

题目大意

给出一个n*n的01方格,现在要求将其中的一些0转换为1,使得每个方格的上下左右格子的数字和为偶数(如果存在的话),求使得最小的转换的个数。

最先想到的是枚举每个方格的状态,非0即1,那么就有2^(n*n)种情况,可见数量之大,必定超时。那么就必须要转换思路。

不难看出这是一个开关问题,就是说可以根据一行的数据,推算出下一行的数据,然后顺次推算出整个格子的数据,之后再来看改变了多少的01序列,求出最小的结果。(稍后会演示推算的算法)。

那么首先就是要枚举第一行的各种情况,这个不难实现。由于题目要求很局限,只是01序列,我们可以采用二进制的方法来依次枚举:

第一行有n个格子,每个格子有2种状态,故需要枚举2^n次。不妨我们先让n为3,看看是如何枚举的。

外层循环是 for i = 0 to (2^3-1)
i = 0 00000000
i = 1 00000001
i = 2 00000010
i = 3 00000011
i = 4 00000100
i = 5 00000101
i = 6 00000110
i = 7 00000111

不难看出 上面的式子中,后3位的情况,正好是我们要枚举的情况。但是我们不能直接取后三位,要想办法,于是问题抓换成:什么时候将某一位(二维数组的某一元素)赋值为1
由于一次只能对1位(二维数组中的一个元素)赋值,我们还需要一层循环, for j = 0 to (n-1) ,再观察下面的式子:

i = 0,j = 0 时 i&(1 << j) =00000000 即为 0
i = 0,j = 1 时 i&(1 << j) = 00000000 即为 0
i = 0,j = 2 时 i&(1 << j) = 00000000 即为0
上面的3式子,可以看做完成了000的赋值。

i = 1,j = 0 时 i&(1 << j) = 00000001 即为1
i = 1,j = 1 时 i&(1 << j) = 00000000 即为 0
i = 1,j = 2 时 i&(1 << j) = 00000000 即为 0
上面的3个式子,可以看做是完成了100的赋值

i = 2,j = 0 时 i&(1 << j) = 00000000 即为 0
i = 2,j = 1 时 i&(1 << j) = 00000000 即为 1
i = 1,j = 2 时 i&(1 << j) = 00000000 即为 0
上面的3个式子,可以看做是完成了010的赋值
……

以此类推,不难看出,这样的2重循环,就完成了对第一行格子的枚举。当然还要注意题目并不允许将1换成0,如果有这样的情况出现,那么要跳过。

现在完成了第一行的枚举。就要根据性质来分别计算第1到n-1行的数据了。依据的性质就是题目中的使得每个方格的上下左右格子的数字和为偶数(如果存在的话),有些读者到此处已经可以大致的写出程序了。大概的算法叙述如下:
我们要算第二行第一列的格子是0还是1,然是并不能直接对这个格子计算,因为他的右边我们还不确定。所以要根据他上一行的格子来计算,由于上一行的格子是在第一行,也就是说他没有上方和左方的格子(这里指的是第一行第一列的格子,若是中间部分的格子,那么只是没有上面的格子,若是第一行最右列的格子,那么他就没有右边和上边的格子),那么求和只需要求右面和下面的格子。而下面的格子又是待求的格子,那么只需要让这个和对2取模,就是这个格子的答案。当然依旧要注意题目并不允许将1换成0。如果有的话,还是要终止这次计算。
(若还有些许不明白,建议看这篇博文:传送门 里面介绍的十分详细)

最后分别求出剩下几行的格子,对比原先的数据,看看改变了多少个01序列。 别忘了上面我们写了一个for循环的枚举,在这么多的情况下,取最小即可,若没有则说明无法打成题目要求,输出-1即可。

代码总览

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define nmax 20
#define INF 0x3f3f3f3f
using namespace std;
int a[nmax][nmax],b[nmax][nmax],n;
int check(int t)
{
    memset(b,0,sizeof(b));
    //根据传入的参数t来枚举第一行
    for(int  i = 0; i<n ;++i){
        if(t & (1<<i) ) b[0][i] = 1;
        else if(a[0][i] == 1) return INF;
    }
    //依次确定第1至n-1行的内容
    for(int i = 1; i<n; ++i){
        for(int j = 0; j<n; ++j){
            int sum = 0;
            if(i != 1) sum+=b[i-2][j];
            if(j != 0) sum+=b[i-1][j-1];
            if(j != n-1) sum+=b[i-1][j+1];
            b[i][j] = sum % 2;
            // 题目中不允许把1 变成 0
            if(a[i][j] == 1 && b[i][j] == 0) return INF;
        }
    }
    int ans = 0;
    for(int i = 0; i<n; ++i)
        for(int j = 0; j<n; ++j)
            if(a[i][j] != b[i][j])
                ans++;
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int cas = 0; cas <T ;cas++){
        //int n;
        scanf("%d",&n);
        for(int i = 0; i<n;++i)
            for(int j = 0; j<n; ++j)
                scanf("%d",&a[i][j]);
        int ans = INF;
        for(int i = 0 ; i < (1<<n);++i){
            ans = min(ans, check(i));
        }
        printf("Case %d: ",cas+1);
        if(ans == INF) printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}
<think>嗯,用户之前问过关于C# Windows Forms代码的问题,现在他们又遇到了一个具体的问题。他们想要在下拉框comboBox2中显示字符串“none”,但目前添加的是System.IO.Ports.Parity的枚举值,可能显示的是枚举的名称,比如Even、Mark等,而用户希望显示的是小写的“none”而不是默认的“None”或者其他形式。 首先,我需要确认用户的需求。他们可能在开发一个串口通信相关的应用程序,因为Parity枚举通常用于串口的校验位设置。用户可能希望下拉列表显示更友好的文本,比如全小写的“none”,而不是枚举的ToString()结果,后者可能是首字母大写的。 接下来,我需要考虑如何在下拉框中显示自定义字符串,同时仍然保留枚举值。通常,处理这种情况的方法有两种:一种是使用数据绑定并设置显示成员和值成员,另一种是手动添加项并处理显示文本。 第一种方法,使用数据绑定,可以将枚举的各个值转换为带有显示名称和实际值的对象,然后将这些对象绑定到ComboBox的DataSource属性,并设置DisplayMember和ValueMember。这样,ComboBox会显示DisplayMember指定的属性,而SelectedValue则获取对应的枚举值。这种方法比较灵活,但需要创建辅助类,可能对于简单的需求来说有点复杂。 第二种方法,用户已经在代码中直接添加了枚举项到Items集合中。这时候,如果直接设置comboBox2.Text = "none",可能在用户选择时无法正确匹配已有的项,因为默认情况下ComboBox会比较项的ToString()结果。由于枚举的None.ToString()返回的是“None”(首字母大写),而用户输入的是小写的“none”,所以直接设置Text属性可能不会生效,或者导致不匹配的问题。 因此,正确的做法应该是修改ComboBox项的显示文本,同时保留其对应的枚举值。这可以通过将每个枚举项包装成一个对象,该对象包含显示文本和对应的值,或者使用FormatString来自动转换显示文本。不过,ComboBox默认不支持直接设置项的显示文本为不同的字符串,除非使用数据绑定或自定义绘制。 另一种方法是在添加枚举项到ComboBox时,将每个项的显示文本转换为小写。但是,直接添加枚举项到Items集合会导致ComboBox显示枚举的ToString()结果,而无法直接修改。因此,可能需要将每个枚举项与对应的显示文本一起存储。例如,可以创建一个包含DisplayText和Value属性的对象,然后将这些对象添加到ComboBox中,设置DisplayMember为“DisplayText”,ValueMember为“Value”。这样,ComboBox会显示DisplayText,而SelectedValue将返回对应的枚举值。 或者,用户可以使用DataBinding的方式,将枚举值转换为需要的字符串。例如,在添加项的时候,手动将枚举值转换为小写字符串,并将枚举值作为项的值。但ComboBox的Items.Add方法只能添加对象,而显示时会调用ToString(),所以可能需要添加匿名对象或者自定义对象。 不过,如果用户希望简单快速地解决这个问题,可能更倾向于直接修改现有代码,而不是重构为数据绑定。这时候,可以尝试在添加枚举项时,修改其显示文本。例如,将每个枚举项的ToString()转换为小写,然后将这些字符串添加到ComboBox,但同时保留对应的枚举值。但是,如果直接添加字符串,那么在获取用户选择的时候,就无法直接获取到枚举值,而需要反向转换字符串回枚举,这可能会引入错误,尤其是当字符串被修改或存在多语言支持时。 因此,更可靠的方法是将每个枚举项与对应的显示文本一起存储,例如使用KeyValuePair或自定义类。这样,ComboBox可以显示小写的文本,而实际的值仍然是枚举值。例如: comboBox2.Items.Add(new { Display = "even", Value = System.IO.Ports.Parity.Even }); // 类似地添加其他项 comboBox2.DisplayMember = "Display"; comboBox2.ValueMember = "Value"; 然后设置comboBox2.Text = "none"时,需要查找对应的项,或者在初始化时设置SelectedItem或SelectedIndex。 但是,用户当前的代码是直接将枚举值添加到Items中,可能希望保持这种方式,同时修改显示文本。这时候,可能需要处理ComboBox的Format事件,在显示项时动态修改文本。例如: private void comboBox2_Format(object sender, ListControlConvertEventArgs e) { if (e.ListItem is System.IO.Ports.Parity parity) { e.Value = parity.ToString().ToLower(); } } 然后,在初始化代码中订阅这个事件: comboBox2.Format += comboBox2_Format; 这样,无论何时ComboBox显示项,都会将枚举值转换为小写字符串显示。同时,用户仍然可以通过SelectedItem获取到实际的枚举值,而不需要额外转换。这种方法可能更简洁,不需要改变现有的添加项的逻辑,只需处理显示格式。 此外,用户想要设置comboBox2.Text = "none",这应该在下拉列表中存在对应的项时自动匹配。使用Format事件后,显示的文本是小写的,所以即使枚举项是Parity.None,显示为“none”,设置Text为“none”应该能够正确匹配到该选项,因为ComboBox会根据显示的文本进行匹配。 但需要注意的是,ComboBox的匹配可能区分大小写,因此可能需要确保输入的文本与显示的文本完全一致。或者,可以通过设置ComboBox的DropDownStyle为DropDownList,强制用户只能选择存在的项,而不能手动输入。 总结,最佳的解决方案是处理Format事件,将枚举值的显示转换为小写,这样既保留了枚举值作为项的实际值,又能够显示用户需要的“none”等小写字符串。同时,设置Text为“none”时,ComboBox会自动找到对应的项,因为显示文本已经转换了。</think>以下是实现下拉框显示小写"none"的完整解决方案,分步骤说明: $$ \text{解决方案流程图} $$ $$ \begin{array}{ccc} \text{添加枚举项} & \rightarrow & \text{设置格式转换} & \rightarrow & \text{设置默认选中} \\ (原始枚举值) & & (显示转小写) & & ("none"匹配显示值) \\ \end{array} $$ **步骤1:添加格式转换事件处理** 在窗体类中添加以下方法实现文本转换: ```csharp private void comboBox2_Format(object sender, ListControlConvertEventArgs e) { if (e.ListItem is System.IO.Ports.Parity parity) { e.Value = parity.ToString().ToLower(); // 转换为全小写 } } ``` **步骤2:绑定事件处理程序** 在构造函数或Load事件中订阅格式事件: ```csharp public Form1() { InitializeComponent(); comboBox2.Format += comboBox2_Format; // 添加此行 } ``` **步骤3:修改初始化代码** 保持原有枚举项添加方式,增加默认值设置: ```csharp // 原添加项代码保持不变 comboBox2.Items.Add(System.IO.Ports.Parity.Even); comboBox2.Items.Add(System.IO.Ports.Parity.Mark); comboBox2.Items.Add(System.IO.Ports.Parity.None); comboBox2.Items.Add(System.IO.Ports.Parity.Odd); comboBox2.Items.Add(System.IO.Ports.Parity.Space); // 设置默认显示值(小写匹配) comboBox2.Text = "none"; // 现在可以正确匹配转换后的显示文本 ``` **实现原理说明**: 通过事件处理实现$\text{显示值} \neq \text{存储值}$的映射关系: $$ \exists f: \text{Parity} \rightarrow \text{string} \quad s.t.\ f(\text{None}) = \text{"none"} $$ **验证方法**: 1. 运行程序观察下拉框显示是否为全小写 2. 检查选中项的Value类型: ```csharp // 获取实际存储值 var selectedParity = (System.IO.Ports.Parity)comboBox2.SelectedItem; // selectedParity 类型仍为Parity枚举 ``` **扩展建议**: 若需要更复杂的显示格式,可使用`FormatString`: ```csharp comboBox2.FormatString = "P: {0}"; // 显示为"P: none" ``` 此方案完整保留了原始枚举值的存储,仅修改显示层表现,符合$\text{MVC模式}$的视图-模型分离原则。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值