Peter studies the theory of relational databases. Table in the relational database consists of values that are arranged in rows and columns.
There are different normal forms that database may adhere to. Normal forms are designed to minimize the redundancy of data in the database. For example, a database table for a library might have a row for each book and columns for book name, book author, and author’s email.
If the same author wrote several books, then this representation is clearly redundant. To formally define this kind of redundancy Peter has introduced his own normal form. A table is in Peter’s Normal Form (PNF) if and only if there is no pair of rows and a pair of columns such that the values in the corresponding columns are the same for both rows.
How to compete in ACM ICPC | Peter | [email protected] |
---|---|---|
How to win ACM ICPC | Michael | [email protected] |
Notes from ACM ICPC champion | Michael | [email protected] |
The above table is clearly not in PNF, since values for 2rd and 3rd columns repeat in 2nd and 3rd rows. However, if we introduce unique author identifier and split this table into two tables — one containing book name and author id, and the other containing book id, author name, and author email, then both resulting tables will be in PNF.
How to compete in ACM ICPC | 1 |
---|---|
How to win ACM ICPC | 2 |
Notes from ACM ICPC champion | 2 |
1 | Peter | [email protected] |
---|---|---|
2 | Michael | [email protected] |
Given a table your task is to figure out whether it is in PNF or not.
Input contains several datasets. The first line of each dataset contains two integer numbers n and m (1 ≤ n ≤ 10000*,*1 ≤ m ≤ 10), the number of rows and columns in the table. The following n lines contain table rows. Each row has m column values separated by commas. Column values consist of ASCII characters from space (ASCII code 32) to tilde (ASCII code 126) with the exception of comma (ASCII code 44). Values are not empty and have no leading and trailing spaces. Each row has at most 80 characters (including separating commas).
For each dataset, if the table is in PNF write to the output file a single word “YES” (without quotes). If the table is not in PNF, then write three lines. On the first line write a single word “NO” (without quotes). On the second line write two integer row numbers r1 and r2 (1 ≤ r1*,r2 ≤ n,r1 ≠ r2), on the third line write two integer column numbers c1 and c2 (1 ≤ c1,c*2 ≤ m,c1 ≠ c2), so that values in columns c1 and c2 are the same in rows r1 and r2.
3 3
How to compete in ACM ICPC,Peter,peter@neerc.ifmo.ru
How to win ACM ICPC,Michael,michael@neerc.ifmo.ru
Notes from ACM ICPC champion,Michael,michael@neerc.ifmo.ru
2 3
1,Peter,peter@neerc.ifmo.ru
2,Michael,michael@neerc.ifmo.ru
3 3
a,b,c
a,c,s
a,b,c
NO
2 3
2 3
YES
NO
1 3
1 2
这个题目采用的是紫皮书上面的方法,自我感觉书上讲的有点不是很清楚。这个题不可以采用四成循环来判断暴力解决,会超时。应该使用map进行映射编号,对于字符串相同的使用一个编号,如果是第一次出现就为它附上一个编号,这样一来只要相同的的字符串都会有相同的编号,我们判断的时候只需要判断两个编号是不是相同的。后面有使用了一个map映射,也是为了使得程序运行的更加快,将每两列中的每一行的编号分别作为键和值存入map,每一行的两个数进行遍历的时候就看看map里面出想过没有,如果过出现过就来一个循环看看是否键和值相同,如果相同就输出结束程序,如果不相同就接着判断。
注意:
- 每一组数据在存入之前一定要进行初始化,以面上一次的结果进行干扰。
- 在后面每两列进行枚举的时候,不可以一次性对键和值进行判断相同。因为可能出现相同的键而值不同,就是(r1,c1)和(r2,c1)相同而(r1,c2)和(r2,c2)不相同,但是map中的键只能对应一个值,因此没法存入map,这也是又采用一个循环从上往下进行一次判断的原因。
我的思路感觉也不是很简单的,脸面的循环也是4个,只不过比暴力速度快了些罢了(耗时3960ms)。或许有更好的办法,暂时每想到。
#include
#include
#include
#include
#include
using namespace std;
map<string, int>ids; //负责映射
map<int, int>id; //用来存储两列的编号
vector<int>str[10010]; //存储编号
int idfind(string s,int n) //编号映射
{
if (!ids.count(s)) //还未进行存储
ids[s] = n; //映射编号
return ids[s]; //返回编号
}
int main()
{
int m, n; //行数和列数
while (cin >> m >> n)
{
getchar(); //吃掉回车
int num = 0,h=0; //初始化
ids.clear(); //初始化
str[0].clear(); //初始化
char t; //一个一个字符的读取
while (1)
{
char s[80] = { 0 };//初始化
for (int i = 0;;i++)
{
t=getchar(); //读取字符
if (t == ',' || t == '\n')break;//判断是否读取完毕
s[i] = t; //存入数组
}
str[h].push_back(idfind(s, num++));//获得映射编号
if (t == '\n')str[++h].clear(); //初始化下一行
if (h == m)break; //读取完毕
}
int flag = 0; //标志位
for (int i = 0;i < n - 1&&!flag;i++) //枚举两列c1
{
for (int j = i+1;j < n&&!flag;j++) //枚举两列c2
{
id.clear(); //初始化
for (int k = 0;k < m&&!flag;k++)//每一行进行判断
{
if (id.count(str[k][i])) //如果发现有相同的键,进一步判断是否存在键和值相同
{
for (int u = 0;u < k&&!flag;u++)//对k行以上的每一行再一次进行判断
{
if (str[u][i] == str[k][i] && str[u][j] == str[k][j])//如果符合条件
{
flag = 1; //标志位置位
cout << "NO" << endl; //输出
cout << u + 1 << " " << k + 1 << endl << i + 1 << " " << j + 1 << endl;
}
}
}
else id[str[k][i]] = str[k][j];//如果没有发现,有相同的键,存入
}
}
}
if (!flag)cout << "YES" << endl;//输出结果
}
}