TopCoder SRM 360 DIV2 500 分题目, 需要排列组合么?需要强力法么?

来源:互联网 发布:C语言整数比较大小 编辑:程序博客网 时间:2024/04/29 14:23
昨天晚上的比赛只做出来 250 分的题目,花了 20  分钟,有所进步,但是后面两题 没做出来,rating  掉到了 926 ,唉 ~~  继续加油吧!

下面是 500 分的题目,初以为需要用排列组合的知识的,但是原来是不需要的,纯粹是个 mathematic trick
简要描述题意如下:
有一个矩阵,行数为 row (不大于 20 ), 列数为 col (不大于 20 ), 元素都是 int 类型( 不大于 50),从这个矩阵中选取一些元素,使得每一行,每一列都最多只有一个元素被选取,将选取的元素求和为 sum 。Larry 说,对于每一种选取的方法, sum 都相等。要求写一个函数,判断 Larry 的话是否正确,正确则返回  "CORRECT", 否则返回  "INCORRECT"。

如果使用强力法( brute force solution ),对于每种选取的方案都求一次和,直到所有方案测试了或者中途求出的和不一样。假设 row >= col ,那么时间复杂度是 O (C (row, col) * col !) = O (row ! /  (row - col) !) ,最大时可达 20 ! ,恐怕超时啦。而且还要生成组合的(我在比赛后才写了个生成组合的函数)。
今天下午看了别人的 discuss ,才恍然大悟,写了代码,在 practice 里面提交,正确!呵呵。


解题的思想是建立在这个事实上:任意一个选取的方案,稍作修改这个方案可以得到下一个方案,继续修改,可以得到所有的方案

对于行数和列数不相等的情况,假设行数较大(反之亦然),必须每一行都相等, Larry 才是正确的。举个反例说,3 × 2 的矩阵,选取的元素用括号括住。这两个方案的 sum 不相等。
(3)     7
 3     (7)
 2      8
========== sum1 = 10
(3)     7
 3      7
 2     (8)
========== sum2 = 11

对于行数和列数相等的情况,必须每两个行(列)向量之差的各分量相等,Larry 才是正确的。举个反例说, 4 × 4 的矩阵,这两个方案的 sum 不相等。
 (1)      2      3      4
  5       6     (7)     8
  9     (10)    11    12
 16     15     14   (13)
===================== sum1 = 31
 (1)      2      3      4
  5       6     (7)     8
  9      10     11   (12)
 16     (15)   14    13
===================== sum2 = 35
行向量 (16, 15, 14, 13) - ( 9, 10, 11, 12) = (7, 5, 3, 11) ,各分量不相等,在以 10 和 13 为对角的子矩阵中, 不选取 10 和 13,改选取另外一组对角 12 和 15,那么可以得到新的选取方案。
而 sum2 - sum1 = ( 12 + 15) - (10 + 13) = (15 - 10) - (13 - 12) = 4 。显然,如果向量差的各分量都相等,那么 sum 不变。那么在生成全部方案的过程中,sum 一直保持不变。
因此只要判断所有的行(列)向量两两之差的各分量都相等即可,但是这样的时间复杂度是 O (row4) ,其实只要每个下标 1 <= i < row,  1 <= j <= col,四个元素满足 (i, j) + (i + 1, j + 1) == (i + 1, j) + (i, j + 1) 这个条件即可,即每个“田”字的对角元素之和相等。这样的时间复杂度是 O (row2) 。

例如这个矩阵就满足这个条件,因此总是满足 sum = 1 + 2 + 3 + 4 + 10 + 20 + 30 + 40 = 110 。
 11 12 13 14
 21 22 23 24
 31 32 33 34
 41 42 43 44

根据以上解法,就不难写出代码了^_^

#include <vector>
#include 
<list>
#include 
<map>
#include 
<set>
#include 
<deque>
#include 
<stack>
#include 
<bitset>
#include 
<algorithm>
#include 
<functional>
#include 
<numeric>
#include 
<utility>
#include 
<sstream>
#include 
<iostream>
#include 
<iomanip>
#include 
<cstdio>
#include 
<cmath>
#include 
<cstdlib>
#include 
<ctime>

using namespace std;

int entry [20] [20];
class SumOfSelectedCells {
public:
    
string hypothesis(vector <string>);
}
;

string SumOfSelectedCells::hypothesis(vector <string> table) {
    
int row = table.size ();
    
int i, j, col = 0;
    
string s;
    
for (i = 0; i < row; i ++)
    
{
        col 
= 0;
        stringstream ss(table [i]);
        
while (ss >> s)
        
{
            sscanf (s.c_str (), 
"%d"& entry [i] [col ++]);
        }

    }

    
if (row > col)    //  每一行必须都相等才能正确,否则存在某个被选位置,去掉它,在同一列里选择一个合适的位置,sum 变化
    {
        
for (i = 0; i < row - 1; i ++)
        
{
            
if (table [i] != table [i + 1])
            
{
                
return "INCORRECT";
            }

        }

        
return "CORRECT";
    }

    
if (row < col)    // 每一列必须都相等才能正确,否则存在某个被选位置,去掉它,在同一行里选择一个合适的位置,sum 变化
    {
        
for (j = 0; j < col - 1; j ++)
        
{
            
for (i = 0; i < col; i ++)
            
{
                
if (entry [i] [j] != entry [i] [j + 1])
                
{
                    
return "INCORRECT";
                }

            }

        }

        
return "CORRECT";
    }

    
// 行列数相等的时候,必须每个“田”字的两组对角之和相等(即每两个行向量,每两个列向量之差的各分量相等)才能正确
    
// 否则存在两个被选位置位于某个矩形的对角,去掉这两个位置,选择另外一组对角,sum 变化
    for (i = 0; i < row - 1; i ++)
    
{
        
for (j = 0; j < col - 1; j ++)
        
{
            
if (entry [i] [j] + entry [i + 1] [j + 1!= entry [i + 1] [j] + entry [i] [j + 1])
            
{
                
return "INCORRECT";
            }

        }

    }

    
return "CORRECT";
}


其中参数
vector <string> table 是字符串数组,先要从中读取矩阵元素到二维数组 entry []  [] 中。
 

在 TC 的竞赛客户端 Arena 里装了个插件 KawigiEdit 2.0 ,会自动帮我生成预编译指令,就是把那些头文件都 include 进来了,还有类方法的声明,以及测试代码,以便本地测试。

另外,1000 分的那道题我用强力法加以优化(其实我觉得像是动态规划),通过了系统测试,并没有超时。最大的输入是 1,000,000 ,而系统花了 800 ms 就搞定了(我的机子上要 1200 ms,比较慢^_^)。牛人们还没有写报告,所以我还没有更好的办法(例如搜索之类),期待 ing。。。



原创粉丝点击