POJ-1002 解题报告
1.题目描述
2.解题过程
按部就班来解题的话,这个题目很容易写出来,这是我的第一个版本的代码,思路是读入一行电话字符串,均转化为整型数字存入vector结构,然后进行排序,顺序统计即可发现重复的电话号码及次数。
1: /*author :lipan2: date: 2013.7.233: email: areslipan@163.com4: */5:
6: #include <iostream>7: #include <vector>8: #include <iterator>9: #include <string>10: #include <algorithm>11:
12: using namespace std;13:
14: int cvtString2Int(string & str);15: void normalizedOutput(int tel,int &count);16: void showDump(vector<int> & telNum);17:
18: bool sortAsc(const int & v1 ,const int & v2)19: {
20: return v1<v2;21: }
22: /*23: void showIntVector(vector<int> & in)24: {25: vector<int>::iterator iter;26: cout<<endl;27: for(iter = in.begin(); iter!= in.end();++iter)28: {29: cout<< *iter<<endl;30: }31: }*/32: int main()33: {
34: int count;35:
36: vector<int> input_tel;37:
38: cin>>count;
39:
40: string curTel;
41: int curTelInt;42: for(int i=0;i<count;++i)43: {
44: cin>> curTel;
45: input_tel.push_back(cvtString2Int(curTel));
46:
47: }
48:
sort(input_tel.begin(),input_tel.end(),sortAsc);49: //showIntVector(input_tel);50: showDump(input_tel);
51: return 1;52: }
53:
54: int cvtString2Int(string & str)55: {
56: int ret = 0;57: for(int i = 0; i< str.length(); ++i)58: {
59: if(str[i] == '-') continue;60:
61: int digit;62: if(str[i]>='0'&&str[i]<='9')63: {
64: digit = str[i] - '0';65: }
66: else67: {
68: if (str[i] <'Z' &&str[i]>'Q')69: {
70: digit = (str[i] - 'Q')/3 + 7;71: }
72: else73: if(str[i]>='A' &&str[i]<'Q')74: {
75: digit = (str[i] - 'A')/3 + 2;76: }
77: }
78:
79: ret = 10 * ret + digit;
80:
81: }
82:
83: return ret;84: }
85:
86: void normalizedOutput(int tel,int &count)87: {
88: cout<<endl;
89: vector<int> result;90: int bitNum = 0;91: while(tel!=0)92: {
93: result.push_back(tel%10);
94: tel /=10;
95: bitNum ++;
96: }
97:
98: int hy = 0;99: while(bitNum<7)100: {
101: cout << '0';102: hy++;
103: if(hy == 3)cout << '-';104: bitNum ++;
105: }
106:
107: vector<int>::reverse_iterator iter;108: for(iter = result.rbegin();iter!= result.rend();++iter)109: {
110: cout<<*iter;
111: hy++;
112: if(hy == 3)cout<<'-';113: }
114:
115: cout<<" "<<count;116:
117: }
118: void showDump(vector<int> & telNum)119: {
120: int flag = 0;121: int count = 1;122:
123: vector<int>::iterator iter;124:
125: int curTel = -1;126: for(iter = telNum.begin(); iter != telNum.end(); ++iter)127: {
128: if(curTel == *iter){count ++;}129: else130: {
131: if(count>1)132: {
133: normalizedOutput(curTel ,count);
134: count = 1;
135: curTel = *iter;
136: if(flag == 0 ) flag = 1;137: }
138: else139: {
140: curTel = *iter;
141: }
142: }
143: }
144:
145: if(count >1)146: {
147: normalizedOutput(curTel,count);
148: if(flag == 0 )flag = 1;149: }
150:
151: if (flag == 0)152: {
153: cout<<"No duplicates.";154: }
155: }
在数据量较小时,这种解法没有问题,但是一旦输入数据变多,程序的效率就会出现大问题,比如这个用这个代码在提交online judge的时候就出现了”Time Limit Exceeded”错误,最开始以为,程序运行效率的瓶颈在于这句话,众所周知,目前排序算法的效率最好也只有O(NlgN),本题最大输入数目为100000,在极端情况下效率是不行的。
sort(input_tel.begin(),input_tel.end(),sortAsc);但是,本着实事求是的态度,我在机器上测了一下这句话的运行时间,测试方法如下(在main函数中加入时间戳):
1: int main()2: {
3: int count;4:
5: vector<int> input_tel;6:
7: cin>>count;
8:
9: string curTel;
10: int curTelInt;11: for(int i=0;i<count;++i)12: {
13: cin>> curTel;
14: input_tel.push_back(cvtString2Int(curTel));
15:
16: }
17: clock_t start, finish;
18: start = clock();
19:
20: sort(input_tel.begin(),input_tel.end(),sortAsc);
21:
22: finish = clock();
23:
24: cout<<"time eclapse: " <<start<<"/"<<finish<<"/"<<(double)(finish-start)<<"/"<<(double)(finish-start)/CLOCKS_PER_SEC <<endl;25:
26: //showIntVector(input_tel);27: showDump(input_tel);
28:
29: finish = clock();
30:
31: cout<<"time eclapse: " <<start<<"/"<<finish<<"/"<<(double)(finish-start)<<"/"<<(double)(finish-start)/CLOCKS_PER_SEC <<endl;32: return 1;33: }
得到的结果大大出乎我的意料,我用73728个数据进行极端测试,结果发现,排序所用时间其实很短,下面是运行结果
可以看到,实际上排序只花了21毫秒,很快就完成了。
那么究竟出了什么问题?
仔细分析觉得可能是online judge把我的输入时间算在总时间里面了,这样的话cin的效率必须考虑,网上搜了一下,发现C++和G++在cin的效率上差别还蛮大的,于是编译器改成了C++,这样一来竟然直接就AC了,汗~(因为程序我是在minGW中调试的,所以submit的时候我一直选G++)。
一般提交时,对于c++程序,用G++或者C++,根据poj中FAQ的描述:
VC++中很多细节实现和C++标准不一样,因此,可以想见,VC++中cin的实现更有效率些或者cin的配置更有效率些。
有了这个想法,就在网上搜索相关资料,发现,其实在G++中也有方式能够使得cin的效率提高,只需在程序开头配置一下就好了,如下:
1: std::ios::sync_with_stdio(false);
具体原因在探寻C++最快的读取文件的方案 中有解释,大概意思就是“对于G++,默认的时候,cin与stdin总是保持同步的,也就是说这两种方法可以混用,而不必担心文件指针混乱,同时cout和stdout也一样,两者混用不会输出顺序错乱。正因为这个兼容性的特性,导致cin有许多额外的开销,如何禁用这个特性呢?只需刚才的语句即可”。总之,VC对cin取消同步与否不敏感,前后效率相同。反过来MINGW则非常敏感,前后效率相差8倍。
一般来讲,linux程序会比window上的程序效率高一点,在用G++和C++都能AC过之后,
发现,同样的程序,时间效率上G++要高很多。这也是一个以后需要注意的问题。
程序写得略长,实际上有些小改进,比如说normlizedoutput函数可以如下改进:
//或者直接写入showDump函数中,毕竟调用函数也有开销。void normalizedOutput(int & tel,int &count){cout<<tel/1000000<<tel/100000%10<<tel/10000%10<<"-";
cout<<tel/1000%10<<tel/100%10<<tel/10%10<<tel%10<<" "<<count<<endl;
}
这么一改进之后,程序效率进一步提高,
最终程序如下:
/*author :lipan
date: 2013.7.23
email: areslipan@163.com
*/
#include <iostream>
#include <vector>
#include <iterator>
#include <string>
#include <algorithm>
using namespace std;int cvtString2Int(string & str);
void normalizedOutput(int &tel,int &count);void showDump(vector<int> & telNum);bool sortAsc(const int & v1 ,const int & v2){return v1<v2;
}int main()
{int count;
vector<int> input_tel;
std::ios::sync_with_stdio(false);cin>>count;string curTel;int curTelInt;
for(int i=0;i<count;++i){cin>> curTel;input_tel.push_back(cvtString2Int(curTel));}sort(input_tel.begin(),input_tel.end(),sortAsc);showDump(input_tel);return 1;
}int cvtString2Int(string & str)
{int ret = 0;
for(int i = 0; i< str.length(); ++i){if(str[i] == '-') continue;int digit;
if(str[i]>='0'&&str[i]<='9'){digit = str[i] - '0';
}else
{if (str[i] <'Z' &&str[i]>'Q'){digit = (str[i] - 'Q')/3 + 7;
}else
if(str[i]>='A' &&str[i]<'Q'){digit = (str[i] - 'A')/3 + 2;
}}ret = 10 * ret + digit;}return ret;
}/*
void normalizedOutput(int &tel,int &count)
{
vector<int> result;
int bitNum = 0;
while(tel!=0)
{
result.push_back(tel%10);
tel /=10;
bitNum ++;
}
int hy = 0;
while(bitNum<7)
{
cout << '0';
hy++;
if(hy == 3)cout << '-';
bitNum ++;
}
vector<int>::reverse_iterator iter;
for(iter = result.rbegin();iter!= result.rend();++iter)
{
cout<<*iter;
hy++;
if(hy == 3)cout<<'-';
}
cout<<" "<<count<<endl;
}*/
void normalizedOutput(int & tel,int &count){cout<<tel/1000000<<tel/100000%10<<tel/10000%10<<"-";
cout<<tel/1000%10<<tel/100%10<<tel/10%10<<tel%10<<" "<<count<<endl;
}void showDump(vector<int> & telNum){int flag = 0;
int count = 1;
vector<int>::iterator iter;
int curTel = -1;
for(iter = telNum.begin(); iter != telNum.end(); ++iter)
{if(curTel == *iter){count ++;}
else
{if(count>1)
{normalizedOutput(curTel ,count);count = 1;curTel = *iter;if(flag == 0 ) flag = 1;
}else
{curTel = *iter;}}}if(count >1)
{normalizedOutput(curTel,count);if(flag == 0 )flag = 1;
}if (flag == 0)
{cout<<"No duplicates.";
}}