紫薯 例题5-9

一、题意

输入一个n行m列的数据库(1<=n<=10000, 1<=m<=10),列用','隔开表示。
要求找到两个不同的行r1, r2和两个不同的列c1, c2使得元素(r1, c1) ==(r2, c1)且(r1, c2) == (r2, c2),即在某两列上下相等的两行。
找到则输出NO,并依次输出r1, r2, c1, c2;否则输出YES。

二、解析

暴力4重遍历显然会超时。
注意到列数最大为10,因此列数可以枚举。
行最多只能用复杂度为O(nlgn)的算法找出相等的两行。这里很快能想到map。
遍历行时,用map存储已经遍历过的行的两行字符串的值(存放在key位上,val位放行号)。这样就可以在O(lgn)时间内找到在当前行之前是否有在这两列相等的行。
从时间复杂度为O(m^2*nlgn)。
算一下数量级也就10^7左右,不会超时。

三、代码

#include <iostream>
#include <string>
#include <sstream>
#include <map>
#define mp make_pair
using namespace std;
const int maxn = 10000 + 5, maxm = 10 + 5;
string db[maxn][maxm];
int n, m;


int main() {
    while(cin >> n >> m) {
        cin.ignore();
        for(int i = 0; i < n; i ++) {
            string str;
            getline(cin, str);
            for(auto& ch : str) {
                if(ch == ' ') ch = '_';
                else if(ch == ',') ch = ' ';
            }
            stringstream ss(str);
            int j = 0;
            while(ss >> db[i][j ++]);
        }
        bool ok = 0;
        for(int c1 = 0; !ok && c1 < m; c1 ++) {
            for(int c2 = c1 + 1; !ok && c2 < m; c2 ++) {
                map<pair<string, string>, int> row_map;
                for(int r2 = 0; !ok && r2 < n; r2 ++) {
                    auto target = mp(db[r2][c1], db[r2][c2]);
                    if(row_map.find(target) != row_map.end()) {
                        ok = 1;
                        cout << "NO\n" << row_map[target] + 1 << " " << r2 + 1 << "\n"
                                       << c1 + 1 << " " << c2 + 1 << endl;
                    }
                    else row_map[mp(db[r2][c1], db[r2][c2])] = r2;
                }
            }
        }
        if(!ok) cout << "YES" << endl;
    }
}

四、归纳

  • 学会分析复杂度,一般1秒内能支持的最大数量级为10^8。可以根据这一点判断自己的算法是否会超时。
  • 若getline(cin, str)之前有用过cin>>x,记得在中间使用cin.ignore()来吃掉多余的'\n'符号。

map有点厉害啊