例题5-9 UVA1592 Database(35行AC代码)
题目大意
给出n*m的表格,判断是否存在两行,他们对应存在两列相同元素,即(r1,c1)=(r2,c1);(r1,c2)=(r2,c2)
思路分析
暴力枚举任意两行两列时间复杂度O(n^4),会超时。因此进行如下优化设计:
枚举任意两列c1,c2,对于这两列,遍历每一行,将两列中的元素用二元组记录,存于map中,若后需遍历发现重复二元组,则表示找到结果,其时间复杂度为O(m*m*n*logn)
若直接用map存储字符串,效率低下,可手动给每个字符串分配编号,map中只需存放其编号即可(二级索引)
map<string, int> idmp; // 字符串->id
vector<int> table[n+1]; // 表中存放字符串对应的id
同时,为了便于输出目标对应的行号,定义如下映射(将二元组置于key是便于查找)
map<pair<int,int>, int> pos; // 两列对应字符串标号->行
- 可用
stringstream与getline实现以,分割字符串
getline(cin, s); stringstream input(s); // 字符串流
while (getline(input, st, ',')) table[i].push_back(getId(st));
AC代码(C++11,map优化,行列同素求解)
#include<bits/stdc++.h>
using namespace std;
int n, m;
string s, st;
map<string, int> idmp; // 字符串->id
int getId(string s) { // 获取字符串id,若已存在,直接返回,否则分配id
if (idmp.find(s) == idmp.end()) idmp.insert({s, idmp.size()}); // 不存在
return idmp[s];
}
int main() {
while (cin >>n >>m) {
getchar(); vector<int> table[n+1]; idmp.clear(); // 初始化!!!
for (int i = 0; i < n; i ++) {
getline(cin, s); stringstream input(s);
while (getline(input, st, ',')) table[i].push_back(getId(st));
}
bool isPNF = true; // 标记是否为PNF
for (int i = 0; i < m-1 && isPNF; i ++) { // 遍历任意两列
for (int j = i+1; j < m && isPNF; j ++) {
map<pair<int,int>, int> pos; // 两列对应字符串标号->行
for (int k = 0; k < n && isPNF; k ++) { // 每一行
if (pos.find({table[k][i],table[k][j]}) == pos.end()) {
pos[{table[k][i],table[k][j]}] = k;
}
else {
printf("NO\n%d %d\n%d %d\n", pos[{table[k][i],table[k][j]}]+1, k+1, i+1, j+1);
isPNF = false;
}
}
}
}
if (isPNF) printf("YES\n");
}
return 0;
}