《算法竞赛入门经典2ndEdition 》例题5-9 数据库(Database, Uva1592)

昨晚上终于把这题AC了,太晚了于是倒头就去睡觉了,现在来写下博客。这题第一开始就没看懂题目,(一直不明白是否会有多个满足题意的行、列,如果有,按题目说的只有两行的话输出具体哪一个满足题意的也并没有指出,于是很是困惑,不过后来找到了WA的原因后觉得可能数据中就是有多个满足题意的,只输出一个就好了)后来还是没懂,按照书上思路写了代码之后WA,于是开始调,一点点的用标程中的代码块替换自己的,替换之后提交,发现WA后,把这段代码该回自己的,再用标程的另一个程序块替换自己程序中等价的部分,最后终于AC了,并且找到了自己的错误。
先发下自己没有修改前的WA版代码:
#include <cstdio>
#include <iostream>
#include <map>
#include <cstring>
#include <vector>
#include <sstream>

using namespace std;

//struct Row
//{
//  int r1,r2;
//  void row()
//  {
//    r1 = r2 = 0;
//  }
//  bool operator < (const Row& rr) const
//  {
//    if(r1 < rr.r1) return true;
//    else return false;
//  }
//};
typedef pair<int,int> PII;
int idcnt;
string str, substring;
vector<int> data[15];
map<string, int> id;
map<PII, int> enumrow;


void strcut(int);

int main()
{
  freopen("New Text Document.txt","r",stdin);
  freopen("Output.txt","w",stdout);
  ios::sync_with_stdio(false);
  int n, m;
  while(cin>>n>>m)//之前此处用了scanf后面用的getline 导致程序出错 
  {
    bool flag = true;
    cin.get(); 
    idcnt = 0;
    for(int i = 0; i < n; i++)
    {
      getline(cin, str);
      //cout<<str<<endl;
      strcut(i);//传递所在行数i 
    }
    for(int i = 0; i < m; i++)
      for(int j = i+1; j < m; j++)
      {
        enumrow.clear();
        for(int k = 1; k < n; k++)
        {
          PII row = make_pair(data[i][k], data[j][k]);
          if(!enumrow.count(row)) 
            enumrow[row] = k;
          else 
          {
            int rr = enumrow[row];
            printf("NO\n%d %d\n%d %d\n", rr+1, k+1, i+1, j+1);
            flag = false;
            break;
          }
        } 
      } 
    if(flag) printf("YES\n");
  }
  return 0;
} 

void strcut(int i)
{
  int j = 0, k, cnt = 0;
  for(k = 0; k < str.length(); k++)
    if(str[k] == ',')//一开始写成了字符与字符串比较 str[k] == ","  
    {
      substring = str.substr(j, k-j);
      j = k+1;
      if( !id.count(substring) ) id[substring] = ++idcnt;
      data[cnt++].push_back(id[substring]); 
    }
  substring = str.substr(j, str.length()-j);
  if( !id.count(substring) ) id[substring] = ++idcnt;
  data[cnt++].push_back(id[substring]);
}

这一版代码我已经修改了一些非程序设计方面的错误,例如scanf与getline同时在程序中出现导致的读入错误(scanf比getline快,于是不能保证在scanf前的getline能正确读入该行数据),还有就是pair的用法,自己之前用了一个结构体,不知道C++中有这样一个pair并且有make_pair函数,还是大大简便了的,这里有我在网上看的关于pair用法的网页:http://blog.csdn.net/huang_xw/article/details/8201671
还是挺不错的

下面是我的下一个修改版:(也是错的)中间我套用了一些刘汝佳的标程来检查。

#include <cstdio>
#include <iostream>
#include <map>
#include <cstring>
#include <vector>
#include <sstream>

using namespace std;

typedef pair<int,int> PII;
int idcnt, n, m;
string str, substring;
vector<int> data[15];
map<string, int> id;
map<PII, int> enumrow;


void strcut(int);
int ID(const string& s) {
  if(!id.count(s)) {
    id[s] = ++idcnt;
  }
  return id[s];
}

void find() {
  for(int c1 = 0; c1 < m; c1++)
    for(int c2 = c1+1; c2 < m; c2++) {
      map<PII, int> d;
      for(int i = 0; i < n; i++) {
        PII p = make_pair(data[c1][i], data[c2][i]);
        if(d.count(p)) {
          printf("NO\n");
          printf("%d %d\n", d[p]+1, i+1);
          printf("%d %d\n", c1+1, c2+1);
          return;
        }
        d[p] = i;
      }
    }
  printf("YES\n");
}

int main()
{
  freopen("New Text Document.txt","r",stdin);
  freopen("Output.txt","w",stdout);
  ios::sync_with_stdio(false);
  while(getline(cin, str))//֮ǰ´Ë´¦ÓÃÁËscanfºóÃæÓõÄgetline µ¼Ö³ÌÐò³ö´í 
  {
    stringstream ss(str);
    if(!(ss >> n >> m)) break;
    bool flag = true;
    cin.get(); 
    idcnt = 0;
    for(int i = 0; i < n; i++)
    {
      getline(cin, str);
      strcut(i);//´«µÝËùÔÚÐÐÊýi 
    }
   // for(int i = 0; i < m; i++)
//      for(int j = i+1; j < m; j++)
//      {
//        enumrow.clear();
//        for(int k = 1; k < n; k++)
//        {
//          PII row = make_pair(data[i][k], data[j][k]);
//          if(!enumrow.count(row)) 
//            enumrow[row] = k;
//          else 
//          {
//            int rr = enumrow[row];
//            printf("NO\n%d %d\n%d %d\n", rr+1, k+1, i+1, j+1);
//            flag = false;
//            break;
//          }
//        } 
//      } 
//    if(flag) printf("YES\n");
    find();
  }
  return 0;
} 

void strcut(int i)
{
  //int j = 0, k, cnt = 0;
//  for(k = 0; k < str.length(); k++)
//    if(str[k] == ',')//Ò»¿ªÊ¼Ð´³ÉÁË×Ö·ûÓë×Ö·û´®±È½Ï str[k] == ","  
//    {
//      substring = str.substr(j, k-j);
//      j = k+1;
//      if( !id.count(substring) ) id[substring] = ++idcnt;
//      data[cnt++].push_back(id[substring]); 
//    }
//  substring = str.substr(j, str.length()-j);
//  if( !id.count(substring) ) id[substring] = ++idcnt;
//  data[cnt++].push_back(id[substring]);

      int lastpos = -1;
      for(int j = 0; j < m; j++) {
        int p = str.find(',', lastpos+1);
        if(p == string::npos) p = str.length();
        data[j].push_back( ID(str.substr(lastpos+1, p - lastpos - 1)) );
        lastpos = p;
      }
}

这个是我的最终AC版错误具体在何处参见下面代码中的注释,就是由于如果是NO,输出完三行后没有停止循环,可能输出了别的解导致(个人认为)。

#include <cstdio>
#include <iostream>
#include <map>
#include <cstring>
#include <vector>
#include <sstream>

using namespace std;


typedef pair<int,int> PII;
int idcnt;
int n, m;
string str, substring;
int data[10010][15];
map<string, int> id;
map<PII, int> enumrow;

void strcut(int);

int main()
{
  freopen("New Text Document.txt","r",stdin);
  freopen("Output.txt","w",stdout);
  ios::sync_with_stdio(false);
  while(cin>>n>>m)//之前此处用了scanf后面用的getline 导致程序出错 
  {
    bool flag = true;
    cin.get(); 
    idcnt = 0;
    for(int i = 0; i < n; i++)
    {
      getline(cin, str);
      strcut(i);//传递所在行数i 
    }
    for(int i = 0; i < m; i++)
    {
      for(int j = i+1; j < m; j++)
      {
        enumrow.clear();
        for(int k = 0; k < n; k++)
        {
          PII row = make_pair(data[k][i], data[k][j]);
          if(!enumrow.count(row)) 
            enumrow[row] = k;
          else 
          {
            int rr = enumrow[row];
            printf("NO\n%d %d\n%d %d\n", rr+1, k+1, i+1, j+1);
            flag = false;
            break;
          }
        }
        if(!flag) break; //最开始没写这句还有下面这个break一直WA 
      }
        if(!flag) break;//这句 我认为或许没有这两个会输出很多组不同的,因此会WA 
    } 
    if(flag) printf("YES\n");
  }
  return 0;
} 

void strcut(int i)
{
  int j = 0, k, cnt = 0;
  for(k = 0; k < str.length(); k++)
    if(str[k] == ',')//一开始写成了字符与字符串比较 str[k] == ","  
    {
      substring = str.substr(j, k-j);
      j = k+1;
      if( !id.count(substring) ) id[substring] = ++idcnt;
      data[i][cnt++] = id[substring]; 
    }
  substring = str.substr(j, str.length()-j);
  if( !id.count(substring) ) id[substring] = ++idcnt;
  data[i][cnt++] = id[substring];
}

下面贴出来刘汝佳标程。
对于标程中用到的string::npos不理解的可以看看下面这两个网址,或许有收获。
http://blog.sina.com.cn/s/blog_7dc67d520101muci.html
http://www.tuicool.com/articles/Unu2Yf

// UVa1592 Database
// Rujia Liu
// 本程序只是为了演示STL各种用法,效率较低。实践中一般用C字符串和哈希表来实现。

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<map>
#include<sstream>
using namespace std;

typedef pair<int,int> PII;

const int maxr = 10000 + 5;
const int maxc = 10 + 5;

int m, n, db[maxr][maxc], cnt;

map<string, int> id;
int ID(const string& s) {
  if(!id.count(s)) {
    id[s] = ++cnt;
  }
  return id[s];
}

void find() {
  for(int c1 = 0; c1 < m; c1++)
    for(int c2 = c1+1; c2 < m; c2++) {
      map<PII, int> d;
      for(int i = 0; i < n; i++) {
        PII p = make_pair(db[i][c1], db[i][c2]);
        if(d.count(p)) {
          printf("NO\n");
          printf("%d %d\n", d[p]+1, i+1);
          printf("%d %d\n", c1+1, c2+1);
          return;
        }
        d[p] = i;
      }
    }
  printf("YES\n");
}


int main() {
  string s;
  while(getline(cin, s)) {
    stringstream ss(s);
    if(!(ss >> n >> m)) break;
    cnt = 0;
    id.clear();
    for(int i = 0; i < n; i++) {
      getline(cin, s);
      int lastpos = -1;
      for(int j = 0; j < m; j++) {
        int p = s.find(',', lastpos+1);
        if(p == string::npos) p = s.length();
        db[i][j] = ID(s.substr(lastpos+1, p - lastpos - 1));
        lastpos = p;
      }
    }
    find();
  }
  return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值