/* A.该题思路: 如果四重循环枚举 r1, r2, c1, c2,那么枚举时间过大,肯定会TLE 解决方法: 只枚举 c1, c2, 然后从上到下扫描各行。每次碰到一个新的行r,把c1 和 c2两列的内容作为一个二维数组存入map,如果map的键值已经存在这个二元组,二元映射到的就是所要求的r1,而当前行就是r2 细节优化: 如何表示由c1 和 c2 构成的二元组? a.直接用两个字符串拼接成一个长字符串(中间用一个不可能出现的字符分隔),但是速度比较慢(因为map再查找元素时,要进行字符串的比较操作) b.(更值得推荐) 在主循环之前先做一个预处理--给所有字符串分配一个编号,则整个数据库中每个单元格都变成了整数,上述二元组就变成了两个整数 B. 一些说明 1. 这题和 UVA - 12096 The SetStack Computer 又异曲同工之妙,链接见下: http://blog.csdn.net/mofushaohua_ln/article/details/77714856 两题的相似之处在于,都采用了为元素编写一个ID,用 ID去对应真正的元素的思想 而两者也有不同的地方,主要在于: UVA - 12096 之所以将 Set 标号,是因为题目是要构造集合的集合并压栈,很难真正去用程序来表示出集合的集合,所以分配ID,把集合等效为其ID,在必要时在通过ID去找到对应的集合,势在必行 而这道题,其实是可以拼接字符串存入map,但关键是string类太慢,string的比较所需的时间可能让人无法接受 所以,一个是因为“不得不为”, 一个是因为“这样更好”,其实是有区别的 2.注意相等的两行两列,列并不一定要相邻,在枚举c1,c2的时候,务必两重循环 3.注意判断两列是否有相等元素时,是通过 map<node,int>findrow 是否存在判断的,所以每次重新枚举两列的组合,都要重新清空findrow 4. 注意在输入新一轮数据之前,有关的 map 和 vector都要先清空,其中IDmap需要逐行清空 5. 如果要用map对自定义类型进行查找,一定不要忘记了重载 < ,相关解释可见: http://bbs.csdn.net/topics/190076742 注意重载小于号时,要设置为class类的常函数 至于为什么要这样,目前 我也没有太明白,但是如果函数外不加const, 会编译错 有关常函数的知识复习可参见这个blog: http://blog.csdn.net/whyglinux/article/details/602329 6.对于输入的问题,其实该博主的这个方法确实略有些麻烦 刘汝佳前辈的代码(他的代码可见本博文法二)中,是用读取整行stinrg,再用stringstream来找逗号的思路,但是《入门经典》P105曾有言: 虽然string和stringstream很方便,但string很慢,sstream更慢,应谨慎使用 所以,其实博主应该是出于对于时限的考虑,虽然这题没卡那么严,但是知道卡得严时,应该怎么处理,也是十分必要的 声明: 代码参考了: http://blog.csdn.net/wowowoc/article/details/40554525 */
#include <iostream> #include <cstdio> #include <map> #include <vector> #include <string> using namespace std; const int ROW = 1e4 + 10; const int COL = 15; int n, m; map<string, int> IDcache; vector<string> Scache; //set cache vector<int> IDmap[ROW]; //IDmap[i][j]表示位置(i+1, j+1)对应的字符串的ID struct node { int x, y; node(int _x, int _y):x(_x), y(_y){ } bool operator < (const node& a) const { return (x < a.x) || (x == a.x && y < a.y); //如果要将自定义的结构体作为map的键值,则必须重载 < 号, } }; map<node, int> findrow; //node对应的是该行在r1, r2列的两个字符串的ID组成的集合,如果这两个字符串已经出现过了,说明找到了,若没有,则将该行对应下标存作 findrow,作为 node(key)对应的的值(value) //找到字符串s的对应ID,如果找不到,就分配一个ID int ID (string s) { if (IDcache.count(s)) return IDcache[s]; Scache.push_back(s); return IDcache[s] = Scache.size() - 1; } void input() { string s; char ch = getchar(); for (int i = 0; i < n; i++) { while (1) { ch = getchar(); //接受输入完n、m之后得到的回车 if (ch == ' ' || ch == ' ') // 说明该行(m个字符串的)读取结束,为当前串分配ID,ID压入IDmap[i] { IDmap[i].push_back(ID(s)); s.clear(); break; } else if (ch != ',') //说明只是读入正常的字符,将它们放入当前串中 { s += ch; } else //,说明该行又读完了一串(一列),分配ID,并将ID压入IDmap[i] { IDmap[i].push_back(ID(s)); s.clear(); } } } } void solve() { int x, y, c1, c2, r; for (c1 = 0; c1 < m; c1++) for (c2 = c1 + 1; c2 < m; c2++) { findrow.clear(); for (r = 0; r < n; r++) { x = IDmap[r][c1]; y = IDmap[r][c2]; node t(x, y); if (!findrow.count(t)) findrow[t] = r; else { cout << "NO" << endl; cout << findrow[t] + 1 << " " << r + 1 << endl; cout << c1 + 1 << " " << c2 + 1 << endl; return; } } } cout << "YES" << endl; } void ReInit() { for (int i = 0; i < n; i++) IDmap[i].clear(); IDcache.clear(); Scache.clear(); } int main() { while (cin >> n >> m) { input(); solve(); ReInit(); } return 0; }