竞赛试题

在线题库:

https://leetcode-cn.com/problemset/all/

在线IDE:
https://www.sourcelair.com/
http://ideone.com/

在线帮助:
http://www.cplusplus.com/
http://msdn.microsoft.com/zh-cn/default.aspx

 

目录

【程序1】求最大的序列和

【程序2】后序遍历

【程序3】the maximum cycle length

【程序4】最大子矩阵

【程序5】Ugly numbers

【程序6】硬币找零问题

【程序7】回文数

【程序8】上台阶方法

【程序9】出栈序列

【程序10】电话号码

【程序11】Hangover

【程序12】Financial Management

【程序13】I Think I Need a Houseboat

【程序14】生理周期

【程序15】DNA Sorting

【程序16】求二进制数中 1 的个数

【程序17】不要被阶乘吓倒

【程序18】寻找发帖“水王”

【程序19】1 的数目

【程序20】寻找最大的 K 个数

【程序21】精确表达浮点数

【程序22】最大公约数问题

【程序23】找乘积结果的十进制表示只含0,1的乘数

【程序24】斐波那契数列

【程序25】寻找数组中的最大值和最小值

【程序26】求高精度幂

【程序27】玛雅历

【程序28】如何判断1个数是不是2的n次方

【程序29】求最长的重复的连续子串


 

【程序1】求最大的序列和

给出一个整数序列S,其中有N个数,定义其中一个非空连续子序列T中所有数的和为T的“序列和”。对于S的所有非空连续子序列T,求最大的序列和。
变量条件:N为正整数,N≤1000000,结果序列和在范围(-2e63,2e63-1)以内。
运行时限:2秒/测试数据
输入格式:第一行为一个正整数N,第二行为N个整数,表示序列中的数。
输出格式:仅一个整数,表示最大序列和。


样例一:
Input.txt
5
1 5 -3 2 4


Output.txt
9

double GetMaxSum(double *a,int n)
{
//x[i]表示以i为最后一个元素的序列的最大和.
//    则递归式为:
//    x[i]=x[i-1]+a[i]   x[i-1]>0
//    x[i]=a[i]          x[i-1]<=0
//    (x[1]=a[1]) 
//如果使用双重循环求解,当N>20000时不满足运行时限
int i,j;
double sum=a[1],mb,mc,*x=new double [n+1];
x[1]=a[1]; //初始化
for(i=2;i<=n;i++)
{
x[i]=(x[i-1]>0)?(x[i-1]+a[i]):a[i];
if(x[i]>sum)sum=x[i];
}
delete []x;
return sum;
}



 

【程序2】后序遍历

给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。
变量条件:二叉树中的结点名称以大写字母表示:A,B,C....最多26个结点。
运行时限:1秒/测试数据。
输入格式:两行,第一行为前序遍历,第二行为中序遍历。
输出格式:若不能根据前序和中序遍历求出后序遍历,输出NO ANSWER;否则输出一行,为后序遍历。


样例一:
Input.txt
ABC
BAC


Output.txt
BCA

 

int inline Getinmid(char firstv,char *mid,int n)
{//获取firstv在mid[n]中的位置
int i;
for(i=0;i<n;i++)
{
if(mid[i]==firstv)return i;
}
return -1;
}


bool CreateTree(BNode **p,char *first,char *mid,int n)
{//递归建造二叉树
if(n!=0)
{
int pm;
pm=Getinmid(first[0],mid,n);
if(pm==-1)return false;

(*p)=new BNode;
(*p)->v=first[0];
(*p)->left=(*p)->right=NULL;
if(!CreateTree(&((*p)->left),first+1,mid,pm)||!CreateTree(&((*p)->right),first+1+pm,mid+pm+1,n-1-pm))return false;
}
return true;
}






 

【程序3】the maximum cycle length

Background
Problems in Computer Science are often classified as belonging to a certain class of problems (e.g., NP, Unsolvable, Recursive). In this problem you will be analyzing a property of an algorithm whose classification is not known for all possible inputs. 


The Problem
Consider the following algorithm: 


 
1. input n


2. print n


3. if n = 1 then STOP


4. if n is odd then   


5. else   


6. GOTO 2




Given the input 22, the following sequence of numbers will be printed 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 
It is conjectured that the algorithm above will terminate (when a 1 is printed) for any integral input value. Despite the simplicity of the algorithm, it is unknown whether this conjecture is true. It has been verified, however, for all integers n such that 0 < n < 1,000,000 (and, in fact, for many more numbers than this.) 
Given an input n, it is possible to determine the number of numbers printed (including the 1). For a given n this is called the cycle-length of n. In the example above, the cycle length of 22 is 16. 
For any two numbers i and j you are to determine the maximum cycle length over all numbers between i and j. 


The Input
The input will consist of a series of pairs of integers i and j, one pair of integers per line. All integers will be less than 1,000,000 and greater than 0. 
You should process all pairs of integers and for each pair determine the maximum cycle length over all integers between and including i and j. 
You can assume that no opperation overflows a 32-bit integer. 


The Output
For each pair of input integers i and j you should output i, j, and the maximum cycle length for integers between and including i and j. These three numbers should be separated by at least one space with all three numbers on one line and with one line of output for each line of input. The integers i and j must appear in the output in the same order in which they appeared in the input and should be followed by the maximum cycle length (on the same line). 


Sample Input
1 10
100 200
201 210
900 1000


Sample Output
1 10 20
100 200 125
201 210 89
900 1000 174

 

int GetLoop(int start,int end)
{//计算1,1000000的最大cycle花费6s时间
int i,sum,max=0;
unsigned int outv; //无符号,否则会溢出
for(i=start;i<=end;i++)
{
sum=0;
outv=i;
while(outv!=1)
{
if(outv%2==0)outv=outv/2;
else outv=3*outv+1;
sum++;
}
sum++;
if(sum>max)max=sum;
}
return max;
}




 

【程序4】最大子矩阵

Background
A problem that is simple to solve in one dimension is often much more difficult to solve in more than one dimension. Consider satisfying a boolean expression in conjunctive normal form in which each conjunct consists of exactly 3 disjuncts. This problem (3-SAT) is NP-complete. The problem 2-SAT is solved quite efficiently, however. In contrast, some problems belong to the same complexity class regardless of the dimensionality of the problem. 


The Problem
Given a 2-dimensional array of positive and negative integers, find the sub-rectangle with the largest sum. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle. A sub-rectangle is any contiguous sub-array of size  or greater located within the whole array. As an example, the maximal sub-rectangle of the array:  
0 -2-70
9 2-62
-4 1-41
-1 80-2
is in the lower-left-hand corner:  
9 2
-4 1
-1 8
and has the sum of 15. 


Input and Output
The input consists of an N*N array of integers. The input begins with a single positive integer N on a line by itself indicating the size of the square two dimensional array. This is followed by N*N integers separated by white-space (newlines and spaces). These N*N integers make up the array in row-major order (i.e., all numbers on the first row, left-to-right, then all numbers on the second row, left-to-right, etc.). N may be as large as 100. The numbers in the array will be in the range [-127, 127]. 
The output is the sum of the maximal sub-rectangle. 


Sample Input
4
0 -2 -7  0 9  2 -6  2
-4  1 -4  1 -1
8  0 -2


Sample Output
15

 

int GetMaxSum2(int **a,int n,int ***m)
{
//直接遍历所有子矩阵的复杂度为n的6次方,不满足时间要求
//设m[i][j][k]表示在矩阵a[x][y]的坐标系中以x=i和x=j的两竖线为两边,以y=k为顶的所有子矩阵的最大值
//     递归式如下:
//     m[i][j][k]=m[i][j][k-1]+Sum{第k行i,j之间的元素}    m[i][j][k-1]>0
//     m[i][j][k]=Sum{第k行i,j之间的元素}                 m[i][j][k-1]<=0                       
//     (m[i][j][1]=Sum{第1行i,j之间的元素})
int i,j,k,l,max=a[1][1];
m[1][1][1]=0;
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j++)
{
m[i][j][1]=0;
for(l=i;l<=j;l++)m[i][j][1]+=a[l][1];
if(m[i][j][1]>max)max=m[i][j][1];
for(k=2;k<=n;k++)
{
m[i][j][k]=0;
if(m[i][j][k-1]>0)
{
m[i][j][k]+=m[i][j][k-1];
for(l=i;l<=j;l++)m[i][j][k]+=a[l][k];
}
else
{
for(l=i;l<=j;l++)m[i][j][k]+=a[l][k];
}
if(m[i][j][k]>max)max=m[i][j][k];
}
}
}
return max;
}




 

【程序5】Ugly numbers

Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence 
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, ... 
shows the first 11 ugly numbers. By convention, 1 is included. 
Write a program to find and print the 1500'th ugly number. 


Input and Output
There is no input to this program. Output should consist of a single line as shown below, with <number> replaced by the number computed. 


Sample output
The 1500'th ugly number is <number>. 

 

int finduglynum(int *x,int n)
{
//如果使用判断每一个数是否是ugly数的方法则优化后计算1500'th需要450s
//本方法直接计算第1500'th个数,只需要1s(优化前需要11s)
//返回值为优化手段,返回值是最小的x[]下标使得对应元素乘以2,3,5后不会与已有元素重复
if(n==1)
{
x[1]=1;
return 1;
}
int i,j,minp,maxp;
unsigned int min,v2,v3,v5; //无符号数,否则溢出
bool f2,f3,f5;
minp=finduglynum(x,n-1);
min=x[n-1]*2;
maxp=minp;
for(i=minp;i<n;i++)
{
v2=x[i]*2;
v3=x[i]*3;
v5=x[i]*5;
f2=f3=f5=true;
for(j=i+1;j<n;j++)
{
if(x[j]==v2)f2=false;
if(x[j]==v3)f3=false;
if(x[j]==v5)f5=false;
}
if(f2&&v2<min)min=v2;
else if(f3&&v3<min)min=v3;
else if(f5&&v5<min)min=v5;
else if(!f2&&!f3&&!f5)maxp=i+1; //小于等于i的都重复
}
x[n]=min;
return maxp;
}



 

【程序6】硬币找零问题

Suppose there are 5 types of coins: 50-cent, 25-cent, 10-cent, 5-cent, and 1-cent. We want to make changes with these coins for a given amount of money. 
For example, if we have 11 cents, then we can make changes with one 10-cent coin and one 1-cent coin, two 5-cent coins and one 1-cent coin, one 5-cent coin and six 1-cent coins, or eleven 1-cent coins. So there are four ways of making changes for 11 cents with the above coins. Note that we count that there is one way of making change for zero cent. 
Write a program to find the total number of different ways of making changes for any amount of money in cents. Your program should be able to handle up to 7489 cents. 


Input  
The input file contains any number of lines, each one consisting of a number for the amount of money in cents. 


Output  
For each input line, output a line containing the number of different ways of making changes with the above 5 types of coins. 


Sample Input  
11
26


Sample Output  
4
13

 

int CoinChange(int n)
{
//x[n][k]为总钱数为n,最大分币为coin[k]的找零方法种数
//     则递归式如下:
//     x[i][j]=x[i-coin[j]][j]+x[i-coin[j]][j-1]+...+x[i-coin[j]][1]
//     (当i<coin[j]时,x[i][j]=0. 当i==coin[j]时,x[i][j]=1)
int i,j,k,coin[6]={0,1,5,10,25,50},(*x)[6]=new int[n+1][6],rst;
for(i=1;i<=n;i++)
{
for(j=1;j<=5;j++)
{
if(i<coin[j])x[i][j]=0;
else if(i==coin[j])x[i][j]=1;
else
{
x[i][j]=0;
for(k=j;k>=1;k--)x[i][j]+=x[i-coin[j]][k];
}
}
}
rst=x[n][1]+x[n][2]+x[n][3]+x[n][4]+x[n][5];
delete []x;
return rst;
}




 

【程序7】回文数

给定一个N进制正整数,把它的各位数字上数字倒过来排列组成一个新数,然后与原数相加,如果是回文数则停止,如果不是,则重复这个操作,直到和为回文数为止。
如果N超过10,使用英文字母来表示那些大于9的数码。例如对16进制数来说,用A表示10,用B表示11,用C表示12,用D表示13,用E表示14,用F表示15。
例如:10进制87则有:
STEP1: 87+78=165
STEP2: 165+561=726
STEP3: 726+627=1353
STEP4: 1353+3531=4884
编写一个程序,输入N(2<=N<=16)进制数M(1<=M<=30000(10进制)),输出最少经过几步可以得到回文数。如果在30步以内(含30步)不可能得到回文数,则输出0。输入的数保证不为回文数。


  输入
第一行一个整数L,代表输入数据的组数。
接下来L行,每行两个整数N,M


  输出 
输出L行,对于每个数据组,按题目要求输出结果,并占一行。


  样例输入
2
10 87
2 110


  样例输出 
4
1

 

bool AddReserve(int *a,int &n,int dg)
{
int i;
bool flag=true;
if(dg<=2) //2进制要特别处理,因为进位后仍有可能是回文数
{
for(i=0;i<(n+1)/2;i++)
{
a[i]=a[n-1-i]=a[i]+a[n-1-i];
}
for(i=0;i<n-1;i++)
{
if(a[i]>2)
{
a[i]-=2;
a[i+1]+=1;
}
}
if(a[n-1]>2)
{
a[n-1]-=2;
a[n]=1;
n++;
}
for(i=0;i<(n+1)/2;i++)
{
if(a[i]!=a[n-1-i])return false;
}
return true;
}
for(i=0;i<(n+1)/2;i++)
{
a[i]=a[n-1-i]=a[i]+a[n-1-i];
}
for(i=0;i<n-1;i++)
{
if(a[i]>dg-1)
{
flag=false;
a[i]-=dg;
a[i+1]+=1;
}
}
if(a[n-1]>dg-1)
{
flag=false;
a[n-1]-=dg;
a[n]=1;
n++;
}
return flag;
}


int GetCount(char *b,int dg)
{
int i=0,count=1,a[300];
while(b[i]!='\0')
{
if(b[i]>='A')a[i]=b[i]-'A'+10;
else a[i]=b[i]-'0';
i++;
}
while(!AddReserve(a,i,dg))count++;
return count;
}




 

【程序8】上台阶方法

有一个n级的台阶,上台阶时一次可以走1,2,或3级.问有多少种上法,打印出所有的上台阶方法.

 

int GetStepNum(int n)
{//递归算法,可以算到n=30
if(n==0)return 1;
if(n<0)return 0;
return (GetStepNum(n-1)+GetStepNum(n-2)+GetStepNum(n-3));
}


int GetStepNum4(int n)
{//动态规划算法,仅受限于int型长度
int i;
int m[300];
int pm;
m[0]=0;
m[1]=1;
m[2]=2;
m[3]=4;
pm=4;
while(pm<=n)
{
m[pm]=m[pm-1]+m[pm-2]+m[pm-3];
pm++;
}
return m[n];
}


void PrintStep(int n,stack<char> &s)
{
if(n==0)
{
//此处可以设引用变量来计数返回上台阶种数,但只能算到n=20
char ch;
stack<char> t;
while(!s.empty())
{
ch=s.top();
s.pop();
t.push(ch);
}
while(!t.empty())
{
ch=t.top();
t.pop();
s.push(ch);
//cout<<ch<<' ';
}
//cout<<endl;
return;
}
if(n>=1){s.push('1');PrintStep(n-1,s);s.pop();}
if(n>=2){s.push('2');PrintStep(n-2,s);s.pop();}
if(n>=3){s.push('3');PrintStep(n-3,s);s.pop();}
}




 

【程序9】出栈序列

打印出一个数组依次进栈后再出栈输出的所有可能序列和序列的个数.

int GetOutStackNum(int n)
{
//动态规划算法
//m[i][j]表示数组中共有i个元素,当原数组中最末的元素在输出序列中处于第j个元素时,所有满足这种条件的输出序列的个数记为m[i][j]
//      则求解m[i][j]的递归式是:
//      m[i][j]=sum{m[i-1][1],m[i-1][2],...m[i-1][j]}
//      (m[1][1]=1;m[1][2]=0;)
//      递归式的意思是:当在原数组的末尾新增加一个元素时,则在原来所有的输出序列中,该元素输出的位置只可能在原最末元素的之前1个或之后任意个的位置.
int m[50][50],i,j,k,sum=0;
m[1][1]=1;
m[1][2]=0;
for(i=2;i<=n;i++)
{
for(j=1;j<=i;j++)
{
m[i][j]=0;
for(k=1;k<=j;k++)
{
m[i][j]+=m[i-1][k];
}
}
m[i][i+1]=0;
}
for(i=1;i<=n;i++) //求n个元素数组的所有输出序列
{
sum+=m[n][i];
}
return sum;
}




void PrintOutStack(stack<int> &s,stack<int> &r,int *a,int n)
{
stack<int> t;
if(n==0)
{//打印结果栈,并弹出输入输出栈中所有剩余元素,此处也可计数统计序列个数
while(!r.empty())
{
t.push(r.top());
r.pop();
}
while(!t.empty())
{
cout<<t.top()<<' ';
r.push(t.top());
t.pop();
}
while(!s.empty())
{
cout<<s.top()<<' ';
t.push(s.top());
s.pop();
}
cout<<endl;
while(!t.empty())
{
s.push(t.top());
t.pop();
}
return;
}
int size=s.size(),i,j;
for(i=0;i<=size;i++)
{//弹出0~size个元素再压入
j=i;
while(j-->0)
{
r.push(s.top());
s.pop();
}
s.push(a[n]);
PrintOutStack(s,r,a,n-1);
s.pop();
j=i;
while(j-->0)
{
s.push(r.top());
r.pop();
}
}
}



【程序10】电话号码

487-3279


Time Limit: 2000MS  Memory Limit: 65536K
Total Submissions: 210658  Accepted: 36720

Description

企业喜欢用容易被记住的电话号码。让电话号码容易被记住的一个办法是将它写成一个容易记住的单词或者短语。例如,你需要给滑铁卢大学打电话时,可以拨打TUT-GLOP。有时,只将电话号码中部分数字拼写成单词。当你晚上回到酒店,可以通过拨打310-GINO来向Gino's订一份pizza。让电话号码容易被记住的另一个办法是以一种好记的方式对号码的数字进行分组。通过拨打必胜客的“三个十”号码3-10-10-10,你可以从他们那里订pizza。

 电话号码的标准格式是七位十进制数,并在第三、第四位数字之间有一个连接符。电话拨号盘提供了从字母到数字的映射,映射关系如下:
A, B, 和C 映射到 2
D, E, 和F 映射到 3
G, H, 和I 映射到 4
J, K, 和L 映射到 5
M, N, 和O 映射到 6
P, R, 和S 映射到 7
T, U, 和V 映射到 8
W, X, 和Y 映射到 9

 Q和Z没有映射到任何数字,连字符不需要拨号,可以任意添加和删除。 TUT-GLOP的标准格式是888-4567,310-GINO的标准格式是310-4466,3-10-10-10的标准格式是310-1010。

 如果两个号码有相同的标准格式,那么他们就是等同的(相同的拨号)
 
你的公司正在为本地的公司编写一个电话号码薄。作为质量控制的一部分,你想要检查是否有两个和多个公司拥有相同的电话号码。


Input

输入的格式是,第一行是一个正整数,指定电话号码薄中号码的数量(最多100000)。余下的每行是一个电话号码。每个电话号码由数字,大写字母(除了Q和Z)以及连接符组成。每个电话号码中只会刚好有7个数字或者字母。

Output

对于每个出现重复的号码产生一行输出,输出是号码的标准格式紧跟一个空格然后是它的重复次数。如果存在多个重复的号码,则按照号码的字典升序输出。如果输入数据中没有重复的号码,输出一行:
No duplicates.


Sample Input
12
4873279
ITS-EASY
888-4567
3-10-10-10
888-GLOP
TUT-GLOP
967-11-11
310-GINO
F101010
888-1200
-4-8-7-3-2-7-9-
487-3279


Sample Output
310-1010 2
487-3279 4
888-4567 3

Source
http://poj.org/problem?id=1002&lang=zh-CN&change=true
East Central North America 1999

 

//在速度上有优化空间,在北大acm上跑1.5s

#include <iostream>
#include <string>
#include <math.h>

using namespace std;

int main()
{
 int i,j,len,rstind;
 bool hasdup=false; //the flag of whether has duplication
 int num; //numbers of input phone-number
 string phoneNumTable="2223334445556667Q77888999Z"; //Table of char to number
 string phone=""; //keep every input phone-number
 int phoneval; //keep every phone-number's int value(as rst[]'s index)
 int* rst=new int[10000000]; //keep the result of every phone-number's duplication times
 //cout<<"hello world";
 //init result table
 for(i=0;i<10000000;i++)
 {
  rst[i]=0;
        //cout<<"init: rst["<<i<<"]="<<rst[i]<<endl;
 }
    //cout<<rst[0]<<rst[1000]<<rst[100000]<<rst[9999999]<<endl;
 //cout<<endl;
 //return 0;
 cin>>num;
 //num=100000;
 for(i=0;i<num;i++)
 {
  cin>>phone;
  //phone="-T-U-T-G-L-O-P-";
  j=phone.find('-');
  //remove '-' in the phone
  while(string::npos != j)
  {
   phone.erase(j, 1); //erase '-'
   j=phone.find('-');
  }
  //cout<<phone<<endl;
  len=phone.length();
  //turn the chars in the phone to numbers
  for(j=0;j<len;j++)
  {
   if(phone[j]>='A')phone[j]=phoneNumTable[phone[j]-'A'];
  }
  //cout<<phone<<endl;

  //turn the phone-number to int value
  phoneval=0;
  for(j=0;j<len;j++)
  {
   phoneval+=(phone[j]-'0')*pow(10.0, len-j-1);
  }
  //get the duplication times
  rst[phoneval]+=1;
        //cout<<phoneval<<' '<<"rst[phoneval]"<<endl;
 }

    //output the result
    for(i=0;i<10000000;i++)
    {
        //cout<<"here: rst["<<j<<"]="<<"rst[j]"<<endl;
        if(rst[i]>1)
        {
            hasdup=true;
            rstind=i;
            //cout<<"xxxxx"<<endl;
            for(j=6;j>=4;j--)
            {
                cout<<rstind/(int)(pow(10.0,j));
                rstind=rstind%(int)(pow(10.0,j));
            }
            cout<<'-';
            for(;j>=0;j--)
            {
                cout<<rstind/(int)(pow(10.0,j));
                rstind=rstind%(int)(pow(10.0,j));
            }
            cout<<' '<<rst[i]<<endl;
        }
    }

    delete[] rst;
    if(!hasdup)cout<<"No duplicates."<<endl;

 return 0;
}



 

//使用STL map存储结果的另解,约1.2S,不过空间少了很多
#include <iostream>
#include <map>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    map<string, int> rst;
    map<string, int>::iterator it;
    const string phoneNumTable="2223334445556667Q77888999Z"; //Table of char to number
    int num; //nums of phoneNum
    int i, j, k;
    int len; //length of input phone numbers
    bool hasdup=false;
    string phoneNum; //input phone numbers
    string standNum="1234567"; //standard phone numbers

    cin>>num;
    for(i=0;i<num;i++)
    {
        cin>>phoneNum;

        len=phoneNum.length();
        //change the input-phone-number to standard-phone-number
        for(j=0,k=0;j<len;j++)
        {
            if(phoneNum[j]=='-')continue;
            if(phoneNum[j]>='A')
            {
                standNum[k]=phoneNumTable[phoneNum[j]-'A'];
            }
            else
            {
                standNum[k]=phoneNum[j];
            }
            k++;
        }
        //cout<<"phoneNum: "<<phoneNum<<", standNum: "<<standNum<<endl;
        //input the result map
        it=rst.find(standNum);
        if(it==rst.end())
        {
            rst[standNum]=1;
            //cout<<"new, and size: "<<rst.size()<<endl;
        }
        else
        {
            rst[standNum]=it->second+1;
            //cout<<"dup, and size: "<<rst.size()<<endl;
        }
    }
    //cout<<"rst_size: "<<rst.size()<<endl;

    //remove the None duplicate phone numbers, and output---because map has already sorted itself when insert elements.
    for(it=rst.begin();it!=rst.end();it++)
    {
        if(it->second!=1)
        {
            hasdup=true;
            cout<<it->first.substr(0,3)<<'-'<<it->first.substr(3,4)<<' '<<it->second<<endl;
        }
    }
    if(!hasdup)
    {
        cout<<"No duplicates."<<endl;
    }

    return 0;
}


 

//转帖个300MS的解
#include <cstdio>
#include <algorithm>
using namespace std;
char s[31];

int Hash() //将输入转换为数字,方便存储
{
    int sum=0;
    for(int i=0,k=0;k<7;i++)
    {
        if(s[i]>='0'&&s[i]<='9')
        {
            sum*=10;k++;
            sum+=(s[i]-'0');
        }
        else if(s[i]>='A'&&s[i]<'Z')
        {
            sum*=10;k++;
            sum+=((s[i]-'A'-(s[i]>'Q'))/3+2);
        }
    }
    return sum;
}

int main()
{

    int n;scanf("%d",&n);
    int data[n];getchar(); //此处data是数组但是大小为n,在G++下可编译运行正常,C++下则不行,需要改为int* data=new int[n]; (末尾需要delete []data;)
    for(int tmp=0;tmp<n;tmp++)
    {
        gets(s);
        data[tmp]=Hash(); //int数组保存输入的各组号码
    }
    sort(data,data+n); //使用STL函数来排序
    bool p=false;n--; //提前n-1,避免下面的data[i]==data[i+1]时出现越界
    for(int i=0,num=1;i<n;i+=num=1)
    {
        while(data[i]==data[i+1]) //判断是否重复和输出一起进行。注意:此处当末尾有多个相同的值时,还是会下标越界
        {
            num++;
            i++;
        }
        if(num>1)
        {
            printf("%03d-%04d %d\n",data[i]/10000,data[i]%10000,num); //使用格式化输出
            p=true;
        }
    }
    if(!p)printf("No duplicates.\n");
    return 0;
}

 

//使用上文思想,使用vector存储实现,并直接将电话号码以string类型保存。运行结果:1S。看来想要快,还是需要避免使用STL的container
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    //map<string, int> rst;
    //map<string, int>::iterator it;
    vector<string> rst;
    vector<string>::iterator it;
    const string phoneNumTable="2223334445556667Q77888999Z"; //Table of char to number
    int num; //nums of phoneNum
    int i, j, k;
    int len; //length of input phone numbers
    bool hasdup=false;
    string phoneNum; //input phone numbers
    string standNum="1234567"; //standard phone numbers
    int rnum=1; //record the duplicate num

    //cout<<"it's it"<<endl;
    cin>>num;
    for(i=0;i<num;i++)
    {
        cin>>phoneNum;

        len=phoneNum.length();
        //change the input-phone-number to standard-phone-number
        for(j=0,k=0;j<len;j++)
        {
            if(phoneNum[j]=='-')continue;
            if(phoneNum[j]>='A')
            {
                standNum[k]=phoneNumTable[phoneNum[j]-'A'];
            }
            else
            {
                standNum[k]=phoneNum[j];
            }
            k++;
        }
        //cout<<"phoneNum: "<<phoneNum<<", standNum: "<<standNum<<endl;
        rst.push_back(standNum);
        //input the result map
        /*
        it=rst.find(standNum);
        if(it==rst.end())
        {
            rst[standNum]=1;
            //cout<<"new, and size: "<<rst.size()<<endl;
        }
        else
        {
            rst[standNum]=it->second+1;
            //cout<<"dup, and size: "<<rst.size()<<endl;
        }
        */
    }
    //cout<<"rst_size: "<<rst.size()<<endl;

    //sort the result
    sort(rst.begin(), rst.end());

    //remove the None duplicate phone numbers, and output---because map has already sorted itself when insert elements.
    for(it=rst.begin();it!=rst.end();it++, rnum=1)
    {
        while(it+1!=rst.end()&&*it==*(it+1))
        {
            rnum++;
            it++;
        }
        if(rnum!=1)
        {
            hasdup=true;
            cout<<(*it).substr(0,3)<<'-'<<(*it).substr(3,4)<<' '<<rnum<<endl;
        }
    }
    if(!hasdup)
    {
        cout<<"No duplicates."<<endl;
    }

    return 0;
}


【程序11】Hangover

 Hangover
Time Limit: 1000MS  Memory Limit: 10000K
Total Submissions: 88384  Accepted: 42699
Description

How far can you make a stack of cards overhang a table? If you have one card, you can create a maximum overhang of half a card length. (We're assuming that the cards must be perpendicular to the table.) With two cards you can make the top card overhang the bottom one by half a card length, and the bottom one overhang the table by a third of a card length, for a total maximum overhang of 1/2 + 1/3 = 5/6 card lengths. In general you can make n cards overhang by 1/2 + 1/3 + 1/4 + ... + 1/(n + 1) card lengths, where the top card overhangs the second by 1/2, the second overhangs tha third by 1/3, the third overhangs the fourth by 1/4, etc., and the bottom card overhangs the table by 1/(n + 1). This is illustrated in the figure below.

 


Input

The input consists of one or more test cases, followed by a line containing the number 0.00 that signals the end of the input. Each test case is a single line containing a positive floating-point number c whose value is at least 0.01 and at most 5.20; c will contain exactly three digits.
Output

For each test case, output the minimum number of cards necessary to achieve an overhang of at least c card lengths. Use the exact output format shown in the examples.
Sample Input

1.00
3.71
0.04
5.19
0.00
Sample Output

3 card(s)
61 card(s)
1 card(s)
273 card(s)
Source

Mid-Central USA 2001

http://poj.org/problem?id=1003

 

#include <cstdio>

using namespace std;

int main()
{
    int rst[521], i, in1, in2, in, ind=2;
    double sum=50;


    //fill the result
    for(i=1; i<=50; i++)
    {
        rst[i]=1;
    }
    //for(ii=0.51, i=51; ii<5.21; ii+=0.01, i++)
    for(i=51; i<521; i++)
    {
        //if(int(ii*100)!=i){printf("my god!%d:%f:%d\n", i, ii, int(ii*100));break;}
        //continue;
        while(true)
        {
            if(sum>=i)
            {
                rst[i]=ind-1;
                break;
            }
            else
            {
                ind++;
                sum+=100.0/ind;
            }
        }
    }

    //for(i=1;i<=520;i++)
    //{
    //    printf("%d:%d\t\t", i, rst[i]);
    //}

    //return the result
    while(true)
    {
        scanf("%d.%d", &in1, &in2);
        in=in1*100+in2;
        if(in==0)break;
        printf("%d card(s)\n", rst[in]);
    }

    return 0;
}

 

【程序12】Financial Management

Financial Management
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 114060 Accepted: 53254
Description

Larry graduated this year and finally has a job. He's making a lot of money, but somehow never seems to have enough. Larry has decided that he needs to grab hold of his financial portfolio and solve his financing problems. The first step is to figure out what's been going on with his money. Larry has his bank account statements and wants to see how much money he has. Help Larry by writing a program to take his closing balance from each of the past twelve months and calculate his average account balance.
Input

The input will be twelve lines. Each line will contain the closing balance of his bank account for a particular month. Each number will be positive and displayed to the penny. No dollar sign will be included.
Output

The output will be a single number, the average (mean) of the closing balances for the twelve months. It will be rounded to the nearest penny, preceded immediately by a dollar sign, and followed by the end-of-line. There will be no other spaces or characters in the output.
Sample Input

100.00
489.12
12454.12
1234.10
823.05
109.20
5.27
1542.25
839.18
83.99
1295.01
1.75
Sample Output

$1581.42
Source
http://poj.org/problem?id=1004
Mid-Atlantic 2001

 

 

 

#include <cstdio>

using namespace std;

    
    
int main()
{

    char* i=new char(0);
    float* in=new float();
    float* sum=new float(0);
    
    while((*i)++<12)
    {
        scanf("%f", in);
        (*sum)+=*in;
    }
    printf("$%.2f", (*sum)/12.0);
    
    return 0;
} 

`

 

 

【程序13】I Think I Need a Houseboat

 

·Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 81479 Accepted: 35184
Description

Fred Mapper is considering purchasing some land in Louisiana to build his house on. In the process of investigating the land, he learned that the state of Louisiana is actually shrinking by 50 square miles each year, due to erosion caused by the Mississippi River. Since Fred is hoping to live in this house the rest of his life, he needs to know if his land is going to be lost to erosion.

After doing more research, Fred has learned that the land that is being lost forms a semicircle. This semicircle is part of a circle centered at (0,0), with the line that bisects the circle being the X axis. Locations below the X axis are in the water. The semicircle has an area of 0 at the beginning of year 1. (Semicircle illustrated in the Figure.)


Input

The first line of input will be a positive integer indicating how many data sets will be included (N). Each of the next N lines will contain the X and Y Cartesian coordinates of the land Fred is considering. These will be floating point numbers measured in miles. The Y coordinate will be non-negative. (0,0) will not be given.
Output

For each data set, a single line of output should appear. This line should take the form of: “Property N: This property will begin eroding in year Z.” Where N is the data set (counting from 1), and Z is the first year (start from 1) this property will be within the semicircle AT THE END OF YEAR Z. Z must be an integer. After the last data set, this should print out “END OF OUTPUT.”
Sample Input

2
1.0 1.0
25.0 0.0
Sample Output

Property 1: This property will begin eroding in year 1.
Property 2: This property will begin eroding in year 20.
END OF OUTPUT.
Hint

1.No property will appear exactly on the semicircle boundary: it will either be inside or outside.
2.This problem will be judged automatically. Your answer must match exactly, including the capitalization, punctuation, and white-space. This includes the periods at the ends of the lines.
3.All locations are given in miles.
Source
http://poj.org/problem?id=1005

Mid-Atlantic 2001

 

 

#include <stdio.h>
//#include <math.h>

//using namespace std;

int main()
{
    int i, num, year;
    double x, y, s;
    double pie = 3.1415926;
    scanf("%d", &num);

    for(i=1; i<=num; i++)
    {
        scanf("%lf %lf", &x, &y);
        s = pie * ( x*x + y*y ) / 2;
        year = (int)( s / 50 ) + 1;
        printf("Property %d: This property will begin eroding in year %d.\n", i, year, s, x, y);
    }
    printf("END OF OUTPUT.\n");

    return 0;
}

 

【程序14】生理周期

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 107253 Accepted: 33261
Description

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为10,下次出现三个高峰同天的时间是12,则输出2(注意这里不是3)。
Input

输入四个整数:p, e, i和d。 p, e, i分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。d 是给定的时间,可能小于p, e, 或 i。 所有给定时间是非负的并且小于365, 所求的时间小于21252。

当p = e = i = d = -1时,输入数据结束。
Output

从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。

采用以下格式:
Case 1: the next triple peak occurs in 1234 days.

注意:即使结果是1天,也使用复数形式“days”。
Sample Input

0 0 0 0
0 0 0 100
5 20 34 325
4 5 6 7
283 102 23 320
203 301 203 40
-1 -1 -1 -1
Sample Output

Case 1: the next triple peak occurs in 21252 days.
Case 2: the next triple peak occurs in 21152 days.
Case 3: the next triple peak occurs in 19575 days.
Case 4: the next triple peak occurs in 16994 days.
Case 5: the next triple peak occurs in 8910 days.
Case 6: the next triple peak occurs in 10789 days.
Source
http://poj.org/problem?id=1006
East Central North America 1999

 

 

 

 

时间:704MS

 

#include <cstdio>

using namespace std;

int main()
{
    int in1, in2, in3, in4, d, i;

    i=1;
    while(true)
    {
        scanf("%d %d %d %d", &in1, &in2, &in3, &in4);
        if(in1 == -1)break;
        /*
        while(in1>in4)
        {
            in1-=23;
        }
        while(in2>in4)
        {
            in2-=28;
        }
        while(in3>in4)
        {
            in3-=33;
        }
        */
        d=in4+1;
        while((d-in1)%23!=0||(d-in2)%28!=0||(d-in3)%33!=0)
        {
            d+=1;
        }

        printf("Case %d: the next triple peak occurs in %d days.\n", i, d-in4);
        i++;
    }

    return 0;
}

 

时间:63MS

 

#include <cstdio>

using namespace std;

int main()
{
    int in1, in2, in3, in4, d, i;

    i=1;
    while(true)
    {
        scanf("%d %d %d %d", &in1, &in2, &in3, &in4);
        if(in1 == -1)break;

        /*
        while(in1>in4)
        {
            in1-=23;
        }
        while(in2>in4)
        {
            in2-=28;
        }
        while(in3>in4)
        {
            in3-=33;
        }
        */
        d=in4+1;
        //求最小的可以整除23的d值,此后以23为增量增长,并不用再判断对23的整除性
        while((d-in1)%23!=0)
        {
            d++;
        }
        while((d-in2)%28!=0||(d-in3)%33!=0)
        {
            d+=23;
        }

        printf("Case %d: the next triple peak occurs in %d days.\n", i, d-in4);
        i++;
    }

    return 0;
}

 

 

 

 

 

时间:0MS

 

#include <cstdio>

using namespace std;

/*
23,28,33最小公倍数为21252
求d,使得:
d除以23余(in1%23)
d除以28余(in2%28)
d除以33余(in3%33)
参考“剩余定理”求解
*/
int main()
{
    int in1, in2, in3, in4, d, i, m1, m2, m3, k1, k2, k3;
    const int M=21252;
    const int M1=924;
    const int M2=759;
    const int M3=644;

    i=1;
    while(true)
    {
        scanf("%d %d %d %d", &in1, &in2, &in3, &in4);
        if(in1 == -1)break;
        m1=in1%23;
        m2=in2%28;
        m3=in3%33;

        k1=M1;
        while(k1%23!=m1)
        {
            k1+=M1;
        }
        k2=M2;
        while(k2%28!=m2)
        {
            k2+=M2;
        }
        k3=M3;
        while(k3%33!=m3)
        {
            k3+=M3;
        }

        d=(k1+k2+k3)%M;

        if(d<=in4)d+=M;

        printf("Case %d: the next triple peak occurs in %d days.\n", i, d-in4);
        i++;
    }

    return 0;
}

 

【程序15】DNA Sorting

 

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 77546 Accepted: 31098
Description

One measure of ``unsortedness'' in a sequence is the number of pairs of entries that are out of order with respect to each other. For instance, in the letter sequence ``DAABEC'', this measure is 5, since D is greater than four letters to its right and E is greater than one letter to its right. This measure is called the number of inversions in the sequence. The sequence ``AACEDGG'' has only one inversion (E and D)---it is nearly sorted---while the sequence ``ZWQM'' has 6 inversions (it is as unsorted as can be---exactly the reverse of sorted).

You are responsible for cataloguing a sequence of DNA strings (sequences containing only the four letters A, C, G, and T). However, you want to catalog them, not in alphabetical order, but rather in order of ``sortedness'', from ``most sorted'' to ``least sorted''. All the strings are of the same length.
Input

The first line contains two integers: a positive integer n (0 < n <= 50) giving the length of the strings; and a positive integer m (0 < m <= 100) giving the number of strings. These are followed by m lines, each containing a string of length n.
Output

Output the list of input strings, arranged from ``most sorted'' to ``least sorted''. Since two strings can be equally sorted, then output them according to the orginal order.
Sample Input

10 6
AACATGAAGG
TTTTGGCCAA
TTTGGCCAAA
GATCAGATTT
CCCGGGGGGA
ATCGATGCAT
Sample Output

CCCGGGGGGA
AACATGAAGG
GATCAGATTT
ATCGATGCAT
TTTTGGCCAA
TTTGGCCAAA
Source
http://poj.org/problem?id=1007
East Central North America 1998

 

 

#include <cstdio>

using namespace std;

int main()
{
    int len, num, i, j, k, tmp;
    char** in;
    int* val;
    int* seq;

    scanf("%d %d", &len, &num);
    in = new char*[num];
    val = new int[num];
    seq = new int[num];

    for(i=0;i<num;i++)
    {
        in[i] = new char[len+1];
        scanf("%s", in[i]);
        seq[i]=i;
        val[i]=0;
        for(j=0;j<len;j++)
        {
            for(k=j+1;k<len;k++)
            {
                if(in[i][k]<in[i][j])val[i]++;
            }
        }

        for(j=i-1;j>=0;j--)
        {
            if(val[j]>val[j+1])
            {
                tmp = val[j];
                val[j] = val[j+1];
                val[j+1] = tmp;
                tmp = seq[j];
                seq[j] = seq[j+1];
                seq[j+1] = tmp;
            }else{
                break;
            }
        }
    }

    for(i=0;i<num;i++)
    {
        printf("%s\n", in[seq[i]]);
        delete in[seq[i]];
    }

    delete in;
    delete val;
    delete seq;

    return 0;
}

 

【程序16】求二进制数中 1 的个数

 

对于一个字节(8bit)的变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能地高。

【解答】使用0-255位的数组做结果映射表,O(1)时间可以得到结果。

对于无符号的整型变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能地高。

来源:《编程之美》2.1节

 

说明:解法2和解法3在同一个数量级上,解法1要慢1/2左右的时间。

 

#include <stdio.h>
#include <limits.h>
#include <sys/time.h>

int one_in_bits(unsigned int num);
int one_in_bits2(unsigned int num);
int one_in_bits3(unsigned int num);

int main(){

    struct timeval tvStart;
    struct timeval tvEnd;
    int one_num=0;
    int diff1, diff2;
    gettimeofday(&tvStart,NULL);
    for(unsigned int i=0;i<=UINT_MAX/1000;i++){
        one_num=one_in_bits(i);
        //one_num=one_in_bits2(i);
        //one_num=one_in_bits3(i);
        printf("%d\n", one_num);
        //if( (diff1=one_in_bits(i)) != (diff2=one_in_bits2(i)) )printf("%d, 1: %d, 2: %d.\n", i, diff1, diff2);
    }
    gettimeofday(&tvEnd,NULL);

    float ms = ((tvEnd.tv_sec - tvStart.tv_sec) * 1000000 + (tvEnd.tv_usec - tvStart.tv_usec)) / 1000.0;

    printf("Run Time:[ %f ]ms.\n", ms);

    return 0;
}

//将1不断左移,然后与输入数进行相与,结果不为0则计数+1
int one_in_bits(unsigned int num){
    int one_num=0;
    int len=sizeof(num)*8;
    for(int i=0;i<len;i++){
        if(num & (0x1)<<i)one_num++;
    }
    return one_num;
}

//将1与输入数相与,结果不为0则计数+1;然后输入数不断右移并继续相与
int one_in_bits2(unsigned int num){
    int one_num=0;
    while(num){
        one_num += (num & 0x1);
        num >>= 1;
    }
    return one_num;
}

//每次执行num&=num-1可以将值为1的最低位进行清除,比如100100会清除右数第3位的1
int one_in_bits3(unsigned int num){
    int one_num=0;
    while(num){
        one_num++;
        num&=num-1; /*消除掉最后的那个1*/
    }
    return one_num;
}

 

 

 

【程序17】不要被阶乘吓倒

 

阶乘(Factorial)是个很有意思的函数,但是不少人都比较怕它,我们来看看两个与阶乘相关的问题:
1. 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3 628 800,N!的末尾有两个0。
2. 求N!的二进制表示中最低位1的位置。

来源:《编程之美》2.2节

 

问题1:2种解法完全不在一个数量级上。

 

#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>
#include <stdio.h>

int fact_0( const unsigned int N);
int fact_0b( const unsigned int N);

int main(){

    struct timeval tvStart;
    struct timeval tvEnd;
    int num=0;
    int diff1, diff2;
    gettimeofday(&tvStart,NULL);
    for(unsigned int i=0;i<=UINT_MAX/10000;i++){
        //num=fact_0(i);
        //num=fact_0b(i);
        //printf("%d\n", num);
        if( (diff1=fact_0(i)) != (diff2=fact_0b(i)) )printf("%d, 1: %d, 2: %d.\n", i, diff1, diff2);
    }
    gettimeofday(&tvEnd,NULL);

    float ms = ((tvEnd.tv_sec - tvStart.tv_sec) * 1000000 + (tvEnd.tv_usec - tvStart.tv_usec)) / 1000.0;

    printf("Run Time:[ %f ]ms.\n", ms);

    return 0;
}

inline int get_5_div(unsigned int n){
    int count = 0;
    while(n%5==0){n/=5;count++;}
    return count;
}

//求N!末尾的0的位数,即阶乘中有多少个10=2*5,因为偶数数量多,所以,每个5可以凑一个10
//本算法中,就是从1到N求解每个数可以分解出多少个5
int fact_0( const unsigned int N){

    int zero_num = 0;
    for(unsigned int i=1;i<=N;i++){
        zero_num += get_5_div(i);
    }
    return zero_num;

}

//本算法中,思想一样,不过不是使用分解方式来求总共5的个数,而是依次求有N以内有多少个5的整数倍数,多少个5^2整数倍的数,多少个5^3整数倍的数...,然后求和这些个数
int fact_0b( const unsigned int N){

    int zero_num = 0;
    for(unsigned int i=5;i<=N;i*=5){
        zero_num += N/i;
    }
    return zero_num;

}


问题2:以下几种解法都在同一个数量级;解法4也和其他解法几乎是一样的速度。

 

 

#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>
#include <stdio.h>

int fact_1_pos(const unsigned int N);
int fact_1_pos2(const unsigned int N);
int fact_1_pos3(unsigned int N);
int one_in_bits4(unsigned int N);

int main(){

    struct timeval tvStart;
    struct timeval tvEnd;
    int num=0;
    int diff1, diff2;
    gettimeofday(&tvStart,NULL);
    for(unsigned int i=0;i<=UINT_MAX/10000;i++){
        //num=fact_1_pos(i);
        //num=fact_1_pos2(i);
        //num=fact_1_pos3(i);
        num=one_in_bits4(i);
        printf("%d\n", num);
        //if( (diff1=fact_1_pos(i)) != (diff2=fact_1_pos2(i)) )printf("%d, 1: %d, 2: %d.\n", i, diff1, diff2);
    }
    gettimeofday(&tvEnd,NULL);

    float ms = ((tvEnd.tv_sec - tvStart.tv_sec) * 1000000 + (tvEnd.tv_usec - tvStart.tv_usec)) / 1000.0;

    printf("Run Time:[ %f ]ms.\n", ms);

    return 0;
}

//求解最低位1的位置,就是求解2进制下末尾有多少个0,也就是求解N的阶乘中有多少个2的乘法因子,1的位置等于2的质因数个数+1
//本算法和上面求解多少个5的乘法因子算法一样
int fact_1_pos(const unsigned int N){

    int pos=1;
    for(unsigned int i=2;i<=N;i*=2){
        pos+=N/i;
    }
    return pos;
}

//和上一个算法一样,只是使用位移操作来代替*2或/2
int fact_1_pos2(const unsigned int N){

    int pos=1;
    int left=1;
    for(unsigned int i=2;i<=N;i<<=1){
        pos+=N>>left;
        left++;
    }
    return pos;
}

//是上一个算法进一步抽象出来的
int fact_1_pos3(unsigned int N){
    int Ret = 1;
    while(N)
    {
        N >>= 1;
        Ret += N;
    }
    return Ret;
}

/*N!含有质因数2 的个数,还等于N 减去 N的二进制表示中1的数目,而末尾1的位置则是2的质因数的个数加1*/
int one_in_bits4(unsigned int num){
    unsigned int all=num;
    int one_num=0;
    while(num){
        one_num++;
        num&=num-1; /*消除掉最后的那个1*/
    }
    return all-one_num+1;
}

 

 

 

【程序18】寻找发帖“水王”

 

Tango 是微软亚洲研究院的一个试验项目。研究院的员工和实习生们都很喜欢在Tango
上面交流灌水。传说,Tango 有一大“水王”,他不但喜欢发贴,还会回复其他ID 发的每个帖
子。坊间风闻该“水王”发帖数目超过了帖子总数的一半。如果你有一个当前论坛上所有帖子
(包括回帖)的列表,其中帖子作者的ID 也在表中,你能快速找出这个传说中的Tango 水
王吗?

 

随着 Tango 的发展,管理员发现,“超级水王”没有了。统计结果表明,有3 个发帖很多
的ID,他们的发帖数目都超过了帖子总数目N 的1/4。你能从发帖ID 列表中快速找出他们
的ID 吗?

 

来源:《编程之美》 2.3节

 

说明:一般思想为先排序,再取len/2的元素,即是个数超过一半总数的元素。扩展问题则是排序后,取1/4,2/4,3/4三个节点的元素。

而使用成对删除的算法可以线形时间完成。

 

#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>
#include <stdio.h>

int find_half(int a[], int len);
int find_three(int a[], int len, int rst[]);

int main(){

    struct timeval tvStart;
    struct timeval tvEnd;
    int num=0;
    int diff1, diff2;
    gettimeofday(&tvStart,NULL);
/*
    for(unsigned int i=0;i<=UINT_MAX/1000;i++){
        num=one_in_bits(i);
        printf("%d\n", num);
        //if( (diff1=one_in_bits(i)) != (diff2=one_in_bits2(i)) )printf("%d, 1: %d, 2: %d.\n", i, diff1, diff2);
    }
*/
    //int a[20]={20,18,15,25,19,25,19,19,19,23,19,19,19,19,2,3,9,19,19,19};
    //num=find_half(a, 20);
    int a[20]={20,18,20,18,20,18,20,18,13,13,13,20,18,20,18,13,13,11,1,13};
    int rst[3];
    num=find_three(a, 20, rst);
    printf("%d %d %d\n", rst[0], rst[1], rst[2]);
    gettimeofday(&tvEnd,NULL);

    float ms = ((tvEnd.tv_sec - tvStart.tv_sec) * 1000000 + (tvEnd.tv_usec - tvStart.tv_usec)) / 1000.0;

    printf("Run Time:[ %f ]ms.\n", ms);

    return 0;
}

/*每次删除两个不同的 ID(不管是否包含“水王”的ID),那么,在剩下的ID 列表中,
“水王”ID 出现的次数仍然超过总数的一半*/
int find_half(int a[], int len){
    //val记录了当前待比较的数值
    //times表示当前重复的个数,每找到一个重复的+1,每找到一个不同的-1
    int val, times=0;
    for(int i=0;i<len;i++){
        if(0 == times){
            val=a[i];
            times++
        }else{
            if(a[i] == val)times++;
            else times--;
        }
    }
    return val;
}

/*寻找发帖超过1/4的3个人,需要每4个不同就删除*/
int find_three(int a[], int len, int rst[]){
    const int N=3;
    //times[i]表示第i个数重复的个数
    //入参的rst[i]记录当前待比较的第i个数值
    int times[N]={0};
    int i,j;
    bool hasEmpty;
    bool isRepeat;
    for(i=0;i<len;i++){
        //判断当前a[i]是否是已经重发的数值
        isRepeat=false;
        for(j=0;j<N;j++){
            if(0!=times[j]&&rst[j]==a[i]){
                //当前循环到的a[i]和待比较的第j个数值一样,所以,不消除,而是累加了times[j]的计数
                times[j]++;
                isRepeat=true;
                break;
            }
        }
        if(isRepeat){
            continue;
        }

        //参见上面注释,以下情况表示a[i]和rst里所有待比较的数值都不同,具体处理还要参考times[i]里是否有计数已经到0,具体参见下面注释
        //判断是否有计数已经为0,即可消减的数值为空了
        hasEmpty=false;
        for(j=0;j<N;j++){
            if(0 == times[j]){
                //times里已经有计数为0,故该计数不能再-1了,只能重置为a[i]数值
                rst[j]=a[i];
                times[j]++;
                hasEmpty=true;
                break;
            }
        }
        if(hasEmpty){
            continue;
        }

        //times里所有计数都不为0,可以所有计数进行-1
        for(j=0;j<N;j++){
            times[j]--;
        }

        //printf("i: %d.times: %d, %d, %d\n", i, times[0], times[1], times[2]);
    }


    return 0;
}

 

 

 

 

 

 

【程序19】1 的数目

给定一个十进制正整数N,写下从1 开始,到N 的所有整数,然后数一下其中出现的所有“1”的个数。例如:N= 2,写下1,2。这样只出现了1 个“1”。N= 12,我们会写下1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12。这样,1的个数是5。问题是:1. 写一个函数f(N),返回1到N之间出现的“1”的个数,比如f(12)=5。2. 在32位整数范围内,满足条件“f(N)= N”的最大的N是多少?

 

来源:《编程之美》2.4节

 

2. 分析:通过归纳可以得出:f(10^n-1)= n * 10^(n-1)

很容易看到,当n = 10^10-1 时候,f(n)=f(10^10-1)=10^10大于n,所以
我们可以猜想,当n 大于某一个数N 时,f(n)会始终比n 大

用数学归纳法可以证明:确实存在这样一个上界。

首先求得该上界值为N = 10^11-1=99 999 999 999,让n 从N 往0 递减,每个分别检
查是否有f(n)= n,第一满足条件的就是我们要求的整数

 

#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>
#include <stdio.h>
#include <math.h>

int many_1(const unsigned int N);

int main(){


    struct timeval tvStart;
    struct timeval tvEnd;
    int num=0;
    unsigned int fit_num=0;
    int diff1, diff2;
    gettimeofday(&tvStart,NULL);
    for(unsigned int i=pow(10,11);i>0;i--){ //此处会出core,因为UINT_MAX=4294967295,pow(10,11)太大了;如果是64位程序,ULONG_MAX则可以满足需求
        num=many_1(i);
        if(num==i){fit_num=i;printf("%d: %d\n", i, num);break;}
        //if(num>i)break;
        //if( (diff1=one_in_bits(i)) != (diff2=one_in_bits2(i)) )printf("%d, 1: %d, 2: %d.\n", i, diff1, diff2);
    }
    printf("The Max Fit Num is: %d\n", fit_num);
    gettimeofday(&tvEnd,NULL);


    float ms = ((tvEnd.tv_sec - tvStart.tv_sec) * 1000000 + (tvEnd.tv_usec - tvStart.tv_usec)) / 1000.0;


    printf("Run Time:[ %f ]ms.\n", ms);


    return 0;
}

int many_1(const unsigned int N){
    unsigned int n, m;
    int z, z10, p;
    int sum = 0;


    for(int i=1, z=1;z <= N;i++){ /*i表示位数,循环计算每位上可以出现1的次数,并求和即为所求*/
        z = (int)pow(10, i);
        z10 = z / 10;
        m = N % z10; /*m表示低位的数值:从1位到i-1位*/
        n = N / z; /*n表示高位的数值:从i+1位到最高位*/
        p = (N / z10) % 10; /*p表示i位上的数值*/
        if( 1 > p ){ /*则i位上出现1的次数由高位决定*/
            sum += n * z10;
        } else if ( 1 < p ){ /*则i位上出现1的次数由高位决定*/
            sum += (n+1) * z10;
        } else { /*则i位上出现1的次数由高位、低位一起决定*/
            sum += n * z10 + (m + 1);
        }
    }


    return sum;
}

 

 

【程序20】寻找最大的 K 个数

有很多个无序的数,我们姑且假定它们各不相等,怎么选出其中最大的
若干个数呢?

来源:《编程之美》 2.5小节

 

该问题常见的两种解法为:1. 先排序再取首K个元素,复杂度为O(N*logN);2. 按降序保留最大的K个数,并遍历输入数组,将每个元素以插入排序的算法插入到最大K个数的数字,复杂度为O(N*K),需要O(K)个空间;
但这两种方法孰优孰劣是由具体情况决定的,即logN > K ?

3. STL实现时,其实是按小顶堆保留最大的K个数,这样可以加快插入的过程,插入的过程即一次整理堆的过程,为logK,故整个时间复杂度为O(N*logK)

使用随机数据测试,方法2和方法3的时间在一个数量级,而且方法2还快些,应该是N,K值和数据的缘故导致stl多了其他消耗,没有体现出优势。

#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>
#include <stdio.h>
#include <algorithm>


using namespace std;


int maxk(int a[], int len, int k);
int maxk2(int a[], int len, int k);


int main(){


    struct timeval tvStart;
    struct timeval tvEnd;
    int num=0;
    int diff1, diff2;
    int len=100;
    int k=10;


    int* a;
    a = new int[len];
    memset(a, 0, len * sizeof(int));
/*
    FILE* franw = fopen("/tmp/random.txt", "w");
    srand(time(0));
    for(int i=0;i<len;i++){
        fprintf(franw, "%d\n", rand()%(len*1000));
    }
    fclose(franw);
*/
    FILE* franr = fopen("/tmp/random.txt", "r");
    for(int i=0;i<len;i++){
        fscanf(franr, "%d", a+i);
    }
    fclose(franr);


    for(int i=0;i<len;i++){
        printf("%d ", a[i]);
    }
    printf("\n");


    gettimeofday(&tvStart,NULL);
/*
    for(unsigned int i=0;i<=UINT_MAX/1000;i++){
        num=one_in_bits(i);
        printf("%d: %d\n", i, num);
        //if( (diff1=one_in_bits(i)) != (diff2=one_in_bits2(i)) )printf("%d, 1: %d, 2: %d.\n", i, diff1, diff2);
    }
*/
    //maxk(a, len, k);
    maxk2(a, len, k);
    gettimeofday(&tvEnd,NULL);


    for(int i=0;i<len;i++){
        printf("%d ", a[i]);
    }
    printf("\n");


    float ms = ((tvEnd.tv_sec - tvStart.tv_sec) * 1000000 + (tvEnd.tv_usec - tvStart.tv_usec)) / 1000.0;


    printf("Run Time:[ %f ]ms.\n", ms);


    delete []a;


    return 0;
}

bool desc(int a, int b){
    return (a > b);
}


int maxk(int a[], int len, int k){


    partial_sort(a, a+k, a+len, desc);


    return 0;
}


int maxk2(int a[], int len, int k){


    int t;
    //sort a[0-(k-1)]
    for(int i=1;i<k;i++){
        for(int j=i;j>0;j--){
            if(a[j]<a[j-1])break;
            t = a[j];
            a[j] = a[j-1];
            a[j-1] = t;
        }
    }


    //sort a[k-len] -> a[0-(k-1)], 一个缩小规模的插入排序
    for(int i=k;i<len;i++){
        if(a[i]<a[k-1])continue;
        t = a[k-1];
        a[k-1] = a[i];
        a[i] = t;
        for(int j=k-1;j>0;j--){
            if(a[j]<a[j-1])break;
            t = a[j];
            a[j] = a[j-1];
            a[j-1] = t;
        }
    }


    return 0;
}

 

【程序21】精确表达浮点数

在计算机中,使用float 或者double 来存储小数是不能得到精确值的。如果
你希望得到精确计算结果,最好是用分数形式来表示小数。有限小数或者无限循
环小数都可以转化为分数。比如:
0.9 = 9/10
0.333(3) = 1/3(括号中的数字表示是循环节)
当然一个小数可以用好几种分数形式来表示。如:
0.333(3) = 1/3 = 3/9
给定一个有限小数或者无限循环小数,你能否以分母最小的分数形式来返回
这个小数呢?如果输入为循环小数,循环节用括号标记出来。下面是一些可能的
输入数据,如0.3、0.30、0.3(000)、0.3333(3333)、……

来源:《编程之美》 2.6节

 

 

 

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <string>

using namespace std;

typedef struct _fraction{
    int integer;
    int numerator;
    int denominator;
} fraction;

fraction get_fract(char*);

int main(){
    fraction rest;
    char* in = new char[500];
    while(true){
        scanf("%s", in);
        rest = get_fract(in);
        printf("%d %d %d\n", rest.integer, rest.numerator, rest.denominator);
    }


    delete []in;
    return 0;
}

int comm_multi_num(int n, int m){
    if( n <= 0 || m <= 0 )return 0;
    int r;
    if(n>m){
        while((r=n%m)!=0){
            n=m;
            m=r;
        }
        return m;
    } else {
        while((r=m%n)!=0){
            m=n;
            n=r;
        }
        return n;
    }
}

int pow_i(int n, int m){
    int sum = 1;
    if(n <= 0 || m < 0)return 0;
    for(int i=0;i<m;i++){
        sum *= n;
    }
    return sum;
}

fraction get_fract(char* str){
    fraction ret;
    char* decstr_c = new char[strlen(str)];
    string decstr = "";
    int integer, decimal=0, circle=0, comm, bit = 0, cbit = 0, numerator, denominator, tden;
    sscanf(str, "%d.%s", &integer, decstr_c);
    decstr = string(decstr_c);
    if(!decstr.empty()){
        bit = decstr.find('(');
        if(bit == 0){
            sscanf(decstr.c_str(), "(%d)", &circle);
            cbit = decstr.substr(bit).length() - 2;
        } else if(bit > 0){
            sscanf(decstr.c_str(), "%d(%d)", &decimal, &circle);
            cbit = decstr.substr(bit).length() - 2;
        } else {
            sscanf(decstr.c_str(), "%d", &decimal);
            bit = decstr.length();
        }
    }
    printf("i: %d, dstr: %s, d: %d, c: %d, bit: %d, cbit: %d\n", integer, decstr.c_str(), decimal, circle, bit, cbit);

    ret.integer = integer;
    tden = pow_i(10, cbit) - 1;
    if(tden == 0)tden = 1;
    denominator = pow_i((int)10, bit) * tden; //此处若使用系统的pow函数,则返回的double类型值再乘法后转int类型值不对,例如bit=2, tden=1, 求得的denominator为99;网上关于double转int丢失精度的一种解决办法是double值+0.5再转int;
    numerator = circle + tden*decimal;

    comm = comm_multi_num(numerator, denominator);
    if(comm > 0){
        ret.denominator = denominator / comm;
        ret.numerator = numerator / comm;
    } else {
        ret.denominator = 1;
        ret.numerator = 0;
    }



    delete []decstr_c;
    return ret;
}

 

【程序22】最大公约数问题

 

不用取模运算,实现求解最大公约数。

来源:《编程之美》 2.7小节

 

#include <stdio.h>

unsigned int comm_multi_num(unsigned int n, unsigned int m);
unsigned int comm_multi_num2(unsigned int n, unsigned int m);

int main(){

    int in1, in2;
    while(true){
        scanf("%d %d", &in1, &in2);
        printf("%d\n", comm_multi_num(in1, in2));
        printf("%d\n", comm_multi_num2(in1, in2));
    }

    return 0;
}

inline bool isEven(unsigned int n){
    return !(n&0x1);
}

//基于《九章算术》“更相减损术”改进
unsigned int comm_multi_num(unsigned int n, unsigned int m){
    unsigned int rst = 1;
    if(0 == n || 0 == m)return 0;
    while(m != n){
        if(isEven(n) && isEven(m)){ //若m,n均为偶数,则f(m,n)=2*f(m/2,n/2)
            n >>= 1;
            m >>= 1;
            rst <<= 1;
        } else if (isEven(n)) { //若m为偶数,n为奇数,则f(m,n)=f(m, n/2)
            n >>= 1;
        } else if (isEven(m)) { //若n为偶数,m为奇数,则f(m,n)=f(m/2,n)
            m >>= 1;
        } else { //若m,n均为奇数(假设n>m),则f(m,n)=f(n-m,m)
            if(n > m){
                n -= m;
            } else {
                m -= n;
            }
        }
        //printf("m: %d, n: %d, rst: %d\n", m, n, rst);
    }
    rst *= m;

    return rst;
}

//去掉除法
unsigned int comm_multi_num2(unsigned int n, unsigned int m){
    unsigned int rst, count2 = 0;
    if(0 == n || 0 == m)return 0;
    while(m != n){
        if(isEven(n) && isEven(m)){ //若m,n均为偶数,则f(m,n)=2*f(m/2,n/2)
            n >>= 1;
            m >>= 1;
            count2++;
        } else if (isEven(n)) { //若m为偶数,n为奇数,则f(m,n)=f(m, n/2)
            n >>= 1;
        } else if (isEven(m)) { //若n为偶数,m为奇数,则f(m,n)=f(m/2,n)
            m >>= 1;
        } else { //若m,n均为奇数(假设n>m),则f(m,n)=f(n-m,m)
            if(n > m){
                n -= m;
            } else {
                m -= n;
            }
        }
        //printf("m: %d, n: %d, rst: %d\n", m, n, rst);
    }
    rst = m;
    rst <<= count2;

    return rst;
}

 

 

 

 

 

【程序23】找乘积结果的十进制表示只含0,1的乘数

任意给定一个正整数N,求一个最小的正整数M,M>1,使得N*M的十进制表示里只含有1和0。

 

扩展问题:

1. 对于任意的N,一定存在M,使得N*M的乘积的十进制表示只有0和1吗?

2. 怎样找出满足题目要求的N和M,使得N*M<2^16,且N+M最大?

 

解析:问题等价于:求一个最小的正整数X,X的十进制表示中只含有1和0,并且X可以被N整除。

X的取值范围为:1、10、11、100、101、110、111、1000...

假设按位数遍历求解,当位数为k时,所有位数小于k的X中,例如1,10,100...等除以N的余数相同,则将这些X值补足到k位(k位上添加1)后,它们除以N的余数仍然相同,为(10^(k-1)) mod N + 1或10或100 mod N。故只需要计算1,10,100...这个序列中最小的数补足到k位后对N的余数。

故按以上思想:将X为k位数的数值按除以N的余数进行分类,并只需要保留余数相同的最小数X。此外,X只含有0和1,而且此方法并不直接使用X的数值,故可以将X只用1的位置表示,例如1000101表示为[0, 2, 6],即1000101=10^0+10^2+10^6。

注:以下代码实现和书上不一致:退出条件不同;测试结果(N=99等)正常。

 

 

扩展问题解答:

1. 不一定存在,程序中会有无解的退出

2. 尝试得知:当M*N为一个常量时,则min(M, N)越小则M+N的值越大;又由于M>1,N>1,故N=2时,M+N的值最大;即小于2^16的,且十进制表示中只有1,0的,最大的偶整数即为所求,而对应的N为2。

 

 

#include <stdio.h>
#include <math.h>

int get_the_num(int n);

int main(){
    int n;
    while(true){
        scanf("%d", &n);
        get_the_num(n);
    }
    return 0;
}

int get_the_num(int n){
    if(n<=1)return -1;

    int rst10 = 10 % n; /*10 mod N*/
    int i, j, k, rstk, trst, rstlen, tmp;
    bool newflag, tflag;
    int** rst = new int*[n]; /*存储各个余数(0至N-1)对应的X最小值,按值从小到大排列*/
    int* len = new int[n]; /*存储rst数组中每个元素的长度*/
    int* rrst = new int[n]; /*存储rst数组中每个元素对应的余数*/
    int* rstmod = new int[n]; /*第i个元素表示X mod N = i的X最小值在rst中的下标*/
    for(i=0;i<n;i++){rst[i]=NULL;len[i]=0;rstmod[i]=-1;rrst[i]=-1;}
    //初始化1位时的结果
    len[0]=1;
    rst[0]=new int[1];
    rst[0][0]=0;
    rrst[0]=1;
    rstmod[1]=0;
    rstlen=1; /*rst数组长度*/

    k=2; /*当前处理的X是k位数*/
    rstk=rst10; /*10^(k-1) mod N*/
    while(true){
        newflag = false;

        tmp = rstlen;
        if(-1==rstmod[rstk]){ //添加10^(k-1)
            newflag = true;
            len[rstlen]=1;
            rst[rstlen]=new int[1];
            rst[rstlen][0]=k-1;
            rrst[rstlen]=rstk;
            rstmod[rstk]=rstlen;
            rstlen++;
        }
        for(i=0;i<tmp;i++){ //从小到大的顺序在第k位加上1进行判断处理
            trst = (rstk+rrst[i]) % n; //计算第k位加1后的余数
            if(-1==rstmod[trst]){ //产生了新余数
                newflag = true;
                len[rstlen] = len[i] + 1;
                rst[rstlen] = new int[len[rstlen]];
                for(j=0;j<len[i];j++)rst[rstlen][j] = rst[i][j];
                rst[rstlen][len[rstlen]-1] = k-1;
                rrst[rstlen] = trst;
                rstmod[trst] = rstlen;
                rstlen++;
            } //else,产生的余数和以前的相同,则最小值仍为以前的值
        }
        rstk = (rst10*rstk) % n; //计算10^k % n的值:10^k mod N = (10^(k-1) mod N) * (10 mod N)
        if(rstmod[0] >=0){ //M值和N值都要>1,此处排除X=N的情况
            i = 0;
            j = 0;
            tflag = false;
            tmp = n;
            while(true){
                if(1 < tmp % 10){ /*N中含有非0,1的数字,肯定X!=N*/
                    tflag = true;
                    break;
                } else if(1 == tmp % 10){
                    if(rst[rstmod[0]][j] != i){ /*当前位不一致,肯定X!=N*/
                        tflag = true;
                        break;
                    } else {
                        j++;
                    }
                }
                tmp = tmp / 10;
                if(0 == tmp)break;
                i++;
            }
            if(!tflag && j == len[rstmod[0]])rstmod[0]=-1; /*若位数不一致,则肯定X!=N,否则X=N*/
            /*
            tmp = 0;
            for(i=0;i<len[rstmod[0]];i++){
                tmp += int(pow(10, rst[rstmod[0]][i])+0.5);
                if(tmp > n || tmp < 0)break; //tmp<0为溢出情况
            }
            //printf("Pass one: %d, len: %d, i: %d.\n", rstmod[0], len[rstmod[0]], i);
            if(i>=len[rstmod[0]])rstmod[0]=-1;
            */
        }
        //printf("k: %d, rstk: %d, rstlen: %d.\n", k, rstk, rstlen);
        if(rstmod[0] >= 0 || !newflag)break;
        k++;
    }

    if(-1 == rstmod[0]){
        printf("That number NOT exist.\n");
    } else {
        printf("That number is: ");
        for(i=rst[rstmod[0]][len[rstmod[0]]-1];i>=0;i--){
            for(j=0;j<len[rstmod[0]];j++){
                if(rst[rstmod[0]][j] == i){printf("1");break;}
            }
            if(j>=len[rstmod[0]])printf("0");
            if(i%3==0)printf(" ");
        }
        printf("\n");
    }

    for(i=0;i<rstlen;i++){
        delete []rst[i];
    }
    delete []rst;
    delete []len;
    delete []rrst;
    delete []rstmod;

    return 0;
}

 

【程序24】斐波那契数列

F(0)=0, F(1)=1, 对于n>1有F(n)=F(n-1) + F(n-2),求F(n)

来源:《编程之美》 2.9小节

 

说明:解法1使用递归比解法2不使用递归慢上2个数量级;而解法3,解法4和解法2在一个数量级上,除了因为n取值较小故不明显外,解法3使用了开方和幂函数,解法4使用了大量的乘法,也应该是原因之一。

 

#include <stdio.h>
#include <math.h>

int fibonacci(int n);
int fibonacci2(int n);
int fibonacci3(int n);
int fibonacci4(int n);

int main(){

    for(int i=0;i<40;i++){
        printf("%d %d %d %d %d\n", i, 1/*fibonacci(i)*/, 1/*fibonacci2(i)*/, 1/*fibonacci3(i)*/, fibonacci4(i));
    }

    return 0;
}

int fibonacci(int n){
    if(0 >= n)return 0;
    if(1 == n)return 1;
    return fibonacci(n-1) + fibonacci(n-2);
}

int fibonacci2(int n){
    if(0 >= n)return 0;
    if(1 == n)return 1;

    int x=0, y=1, z=1;
    while((n--) > 2){
        x = y;
        y = z;
        z += x;
    }

    return z;
}

int fibonacci3(int n){
    if(0 >= n)return 0;
    if(1 == n)return 1;
    double t = sqrt(5);
    return (int)((t/5*pow((1+t)/2, n) - t/5*pow((1-t)/2, n)) + 0.5);
}

//实现一维数组表示的l*l的矩阵的乘法,c=a*b为返回值
int matrix_multiplication(const int a[], const int b[], int c[], int l){
    int i,j,k,t;
    if(l<=0)return -1;
    for(i=0;i<l;i++){
        for(j=0;j<l;j++){
            //计算c[i,j]=a[i,0]*b[0,j]+a[i,1]*b[1,j]+...+a[i,l-1]*b[l-1,j]的值
            t = i*l + j;
            c[t] = 0;
            for(k=0;k<l;k++){
                c[t] += a[i*l+k]*b[k*l+j];
            }
        }
    }
    return 0;
}

//对于二阶递推数列,存在一个2*2的矩阵A使得:( F(n) F(n-1) ) = ( F(n-1) F(n-2) ) * A ,求解得A=(1 1, 1 0)
//于是代入求解问题转换为:( F(n) F(n-1) ) = ( F(1) F(0) ) * A^(n-1),求得A的方幂问题即获得解答
//记n的二进制表示为:a<k>*2^k+a<k-1>*2^(k-1)+...+a<1>*2+a<0>,其中a<i>表示下标为i,a<i>=0或1
//则代入并展开得:A^n=(A^(2^k))^a<k> * (A^(2^(k-1)))^a<k-1> * ... * (A^2)^a<1> * A^a<0>,转换为求log(n)次乘法。其中的A^(2^k)=(A^(2^(k-1)))^2=(A^(2^(k-1)) * A^(2^(k-1)))来积累求解即可。总的时间复杂度为log(n)。
int fibonacci4(int n){
    if(0 >= n)return 0;
    if(1 == n)return 1;

    int a[4] = {1, 1, 1, 0}; //表示矩阵A=(1 1, 1 0)
    int ak[4] = {1, 1, 1, 0}; //积累保存A^(2^k),初始值=A
    int ak2[4] = {0}; //同上,和ak轮换着用
    int rst[4] = {1, 0, 0, 1}; //保存A^n,初始值为单位矩阵
    int rst2[4] = {0}; //同上,和rst轮换着用

    bool useak = true; //在使用ak还是ak2
    bool userst = true; //在使用rst还是rst2

    //求A^(n-1)
    n--;
    while(n){
        if(n&0x1){ //最低bit位为1,需要计算A^n *= A^(2^k)
            if(userst){
                if(useak){
                    matrix_multiplication(ak, rst, rst2, 2);
                } else {
                    matrix_multiplication(ak2, rst, rst2, 2);
                }
                userst = false;
            } else {
                if(useak){
                    matrix_multiplication(ak, rst2, rst, 2);
                } else {
                    matrix_multiplication(ak2, rst2, rst, 2);
                }
                userst = true;
            }
        }
        //计算A^(2^k) = A^(2^(k-1)) * A^(2^(k-1))
        if(useak){
            matrix_multiplication(ak, ak, ak2, 2);
            useak = false;
        } else {
            matrix_multiplication(ak2, ak2, ak, 2);
            useak = true;
        }
        //最低bit位处理完毕,n右移1位
        n >>= 1;
    }

    //计算F(n) = F(1)*A'[0,0] + F(0)*A'[1,0] ,A'即为以上求得的A^(n-1)
    if(userst){
        return rst[0];
    } else {
        return rst2[0];
    }
}

 

 

 

【程序25】寻找数组中的最大值和最小值

 

对于一个由N个整数组成的数组,需要比较多少次才能把最大值和最小值找出来?

 

解答:

常规方法遍历一遍数组,和保存最大值、最小值的变量进行比较,这种方法最坏情况还是比较2N次,平均情况大约是比较N+0.5N=1.5N次。

分治法可以计算出,比较次数为1.5N。

实际上,有另一种解法也可以保证比较次数为1.5N,就是将数组每2个一组先比较出较大值和较小值,然后再分别去更新最大值和最小值。这种方法通过先比较出较大值和较小值的方法从而可以保证最坏的情况下仍为比较1.5N次。

 

#include <stdio.h>

int getMaxMin(int a[], int n, int& max, int& min);
int getMaxMin2(int a[], int n, int& max, int& min);

int main(){
    int a[20]={10,20,23,45,12,11,8,18,81,2,99,111,52,93,23,18,12,42,82,103};
    //int a[20]={10,20,23,45,12,11,8,18,81,2,99,111,52,93,23,18,12,42,82,903};
    //int a[20]={910,20,23,45,12,11,8,18,81,2,99,111,52,93,23,18,12,42,82,103};
    //int a[20]={10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10};
    int max, min;
    getMaxMin(a, 20, max, min);
    printf("%d %d\n", max, min);
    getMaxMin2(a, 20, max, min);
    printf("%d %d\n", max, min);
    return 0;
}

//分治法
int getMaxMin(int a[], int n, int& max, int& min){
    if(0 >= n)return -1;
    if(1 == n){
        max=min=a[0];
        return 0;
    }
    if(2 == n){
        if(a[0]>=a[1]){
            max = a[0];
            min = a[1];
        } else {
            max = a[1];
            min = a[0];
        }
        return 0;
    }
    int max1, max2, min1, min2, m=n/2;
    getMaxMin(a, m, max1, min1);
    getMaxMin(a+m, n-m, max2, min2);
    max = max1 >= max2 ? max1 : max2;
    min = min1 < min2 ? min1 : min2;
    return 0;
}

//先两两比较出较大值和较小值再分别去更新最大值和最小值,从而保证了最坏情况下也是1.5N次比较
int getMaxMin2(int a[], int n, int& max, int& min){
    if(0>=n)return -1;
    if(0==n%2){
        if(a[0]>=a[1]){
            max = a[0];
            min = a[1];
        } else {
            max = a[1];
            min = a[0];
        }
    } else {
        max = min = a[0];
    }
    for(int i=(n%2 ? 1 : 2);i<n;i+=2){
        if(a[i]>=a[i+1]){
            max = max >= a[i] ? max : a[i];
            min = min < a[i+1] ? min : a[i+1];
        } else {
            max = max >= a[i+1] ? max : a[i+1];
            min = min < a[i] ? min : a[i];
        }
    }
    return 0;
}

 

【程序26】求高精度幂

 

 

Description

对数值很大、精度很高的数进行高精度计算是一类十分常见的问题。比如,对国债进行计算就是属于这类问题。

现在要你解决的问题是:对一个实数R( 0.0 < R < 99.999 ),要求写程序精确计算 R 的 n 次方(Rn),其中n 是整数并且 0 < n <= 25。
Input

T输入包括多组 R 和 n。 R 的值占第 1 到第 6 列,n 的值占第 8 和第 9 列。
Output

对于每组输入,要求输出一行,该行包含精确的 R 的 n 次方。输出需要去掉前导的 0 后不要的 0 。如果输出是整数,不要输出小数点。
Sample Input

95.123 12
0.4321 20
5.1234 15
6.7592 9
98.999 10
1.0100 12
Sample Output

548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201
Source

East Central North America 1988

http://poj.org/problem?id=1001

 

说明:使用大整数运算库去计算会出现超出时间限制(500MS以上),考虑到本题的限制为5位有效数字,最多25次幂,可以特殊实现。

下面实现的用时:0MS,内存404K

 

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

int main()
{
    char rStr[7];
    int n, r, pos, len, i, j, c, t, t2, ct;
    int rst[125];

    while(scanf("%s %d", rStr, &n) != EOF){

        //将输入字符串转换为整数,并记录小数点的位置
        pos = 0; //记录有多少位小数
        r = 0; //底先取整
        len = strlen(rStr);
        for(i=0;i<125;i++)rst[i]=0;
        j=len-1; //计算rst的初始结果的最高位的位置
        for(i=0;i<len;i++){
            if(rStr[i]=='.'){
                j--;
            }
        }
        for(i=0;i<len;i++,j--){ //求解r
            if(rStr[i]=='.'){
                pos=len-i-1;
                j++;
                continue;
            }
            r*=10;
            r+=rStr[i]-'0';
            rst[j]=rStr[i]-'0';
        }
        //printf("%d %d %d\n", r, n, pos);

        //通过乘以n-1次来求解n次幂
        for(i=0;i<n-1;i++){
            c=0;
            for(j=0;j<125;j++){
                if(rst[j]!=0||c!=0){
                    t=rst[j]*r;
                    t2=t/10;
                    ct=c;
                    c/=10;
                    rst[j]=t-10*t2+ct-10*c;
                    c+=t2;
                    if(rst[j]>19){
                        rst[j]-=20;
                        c+=2;
                    }else if(rst[j]>9){
                        rst[j]-=10;
                        c++;
                    }
                }
            }
        }
        for(j=124;j>=0;j--){
            if(rst[j]!=0)break;
        }
        len=j+1; //结果的长度
        if(len==0){
            printf("0\n");
            continue;
        }
        for(j=0;j<125;j++){
            if(rst[j]!=0)break;
        }
        t2=j; //最后一位非0的数的位置
        t=pos*n; //结果中小数点的位置
        if(len>t){
            for(j=len-1;j>t-1;j--){
                printf("%d", rst[j]);
            }
            if(j>=t2)printf(".");
            for(;j>=t2;j--){
                printf("%d", rst[j]);
            }
        } else{
            printf(".");
            for(j=t-1;j>=t2;j--){
                printf("%d", rst[j]);
            }
        }
        printf("\n");

    }
    return 0;
}

 

【程序27】玛雅历

 

 

Description

上周末,M.A. Ya教授对古老的玛雅有了一个重大发现。从一个古老的节绳(玛雅人用于记事的工具)中,教授发现玛雅人使用了一个一年有365天的叫做Haab的历法。这个Haab历法拥有19个月,在开始的18个月,一个月有20天,月份的名字分别是pop, no, zip, zotz, tzec, xul, yoxkin, mol, chen, yax, zac, ceh, mac, kankin, muan, pax, koyab, cumhu。这些月份中的日期用0到19表示。Haab历的最后一个月叫做uayet,它只有5天,用0到4表示。玛雅人认为这个日期最少的月份是不吉利的,在这个月法庭不开庭,人们不从事交易,甚至没有人打扫屋中的地板。

因为宗教的原因,玛雅人还使用了另一个历法,在这个历法中年被称为Tzolkin(holly年),一年被分成13个不同的时期,每个时期有20天,每一天用一个数字和一个单词相组合的形式来表示。使用的数字是1~13,使用的单词共有20个,它们分别是:imix, ik, akbal, kan, chicchan, cimi, manik, lamat, muluk, ok, chuen, eb, ben, ix, mem, cib, caban, eznab, canac, ahau。注意:年中的每一天都有着明确唯一的描述,比如,在一年的开始,日期如下描述: 1 imix, 2 ik, 3 akbal, 4 kan, 5 chicchan, 6 cimi, 7 manik, 8 lamat, 9 muluk, 10 ok, 11 chuen, 12 eb, 13 ben, 1 ix, 2 mem, 3 cib, 4 caban, 5 eznab, 6 canac, 7 ahau, ,8 imix, 9 ik, 10 akbal ……也就是说数字和单词各自独立循环使用。

Haab历和Tzolkin历中的年都用数字0,1,……表示,数字0表示世界的开始。所以第一天被表示成:
Haab: 0. pop 0
Tzolkin: 1 imix 0
请帮助M.A. Ya教授写一个程序可以把Haab历转化成Tzolkin历。
Input

Haab历中的数据由如下的方式表示:
日期. 月份 年数

输入中的第一行表示要转化的Haab历日期的数据量。下面的每一行表示一个日期,年数小于5000。
Output

Tzolkin历中的数据由如下的方式表示:
天数字 天名称 年数

第一行表示输出的日期数量。下面的每一行表示一个输入数据中对应的Tzolkin历中的日期。
Sample Input

3
10. zac 0
0. pop 0
10. zac 1995
Sample Output

3
3 chuen 0
1 imix 0
9 cimi 2801
Source

Central Europe 1995

http://poj.org/problem?id=1008

 

效率:404K 16MS

 

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

int main()
{
    int lineNum, i, fmon, fday, fyear, tday1, tday2, tyear, dayCount;
    char tStr;
    char fmonStr[7];
    //imix, ik, akbal, kan, chicchan, cimi, manik, lamat, muluk, ok, chuen, eb, ben, ix, mem, cib, caban, eznab, canac, ahau
    char dayNameTable[20][9]={"imix", "ik", "akbal", "kan", "chicchan", "cimi", "manik", "lamat", "muluk", "ok", "chuen", "eb", "ben", "ix", "mem", "cib", "caban", "eznab", "canac", "ahau"};
    //for(i=0;i<19;i++)printf("%s ",dayNameTable[i]);

    scanf("%d", &lineNum);
    printf("%d\n", lineNum);

    for(i=0;i<lineNum;i++){
        scanf("%d %c %s %d", &fday, &tStr, fmonStr, &fyear);
        //pop, no, zip, zotz, tzec, xul, yoxkin, mol, chen, yax, zac, ceh, mac, kankin, muan, pax, koyab, cumhu, uayet
        if(strcmp(fmonStr,"pop")==0){
            fmon=0;
        } else if(strcmp(fmonStr,"no")==0){
            fmon=1;
        } else if(strcmp(fmonStr,"zip")==0){
            fmon=2;
        } else if(strcmp(fmonStr,"zotz")==0){
            fmon=3;
        } else if(strcmp(fmonStr,"tzec")==0){
            fmon=4;
        } else if(strcmp(fmonStr,"xul")==0){
            fmon=5;
        } else if(strcmp(fmonStr,"yoxkin")==0){
            fmon=6;
        } else if(strcmp(fmonStr,"mol")==0){
            fmon=7;
        } else if(strcmp(fmonStr,"chen")==0){
            fmon=8;
        } else if(strcmp(fmonStr,"yax")==0){
            fmon=9;
        } else if(strcmp(fmonStr,"zac")==0){
            fmon=10;
        } else if(strcmp(fmonStr,"ceh")==0){
            fmon=11;
        } else if(strcmp(fmonStr,"mac")==0){
            fmon=12;
        } else if(strcmp(fmonStr,"kankin")==0){
            fmon=13;
        } else if(strcmp(fmonStr,"muan")==0){
            fmon=14;
        } else if(strcmp(fmonStr,"pax")==0){
            fmon=15;
        } else if(strcmp(fmonStr,"koyab")==0){
            fmon=16;
        } else if(strcmp(fmonStr,"cumhu")==0){
            fmon=17;
        } else if(strcmp(fmonStr,"uayet")==0){
            fmon=18;}
        //printf("%d %s %d\n", fday, fmonStr, fyear);

        dayCount=fyear*365+fmon*20+fday; //0年0月0天记为第1天,dayCount求解的是天数-1,便于计算转换后的日期有整除的情况

        tyear=dayCount/260;
        dayCount%=260;
        tday1=dayCount%13+1;
        tday2=dayCount%20;

        printf("%d %s %d\n", tday1, dayNameTable[tday2], tyear);
    }
    return 0;
}

 

【程序28】如何判断1个数是不是2的n次方

2的n次方,即2进制表示,除了最高位为1,其他均为0;

1)最直接的思路是使用位移和与操作来确认最高位为1;

2)利用“num&num-1可以消除掉二级制表示里最低位的1”这个规则进行快速求解;

#include <iostream>
using namespace std;

//最直接的思路是使用位移和与操作来确认最高位为1;
bool isNthPowerOfTwo1(int n){
	if(n<1){
		return false;
	}
	
	int i=1;
	int base=0x1;
	while(base<n){
		if((n&base) != 0){
			return false;
		}
		base=0x1<<i++;
	}
	return true;
}

//利用“num&num-1可以消除掉二级制表示里最低位的1”这个规则进行快速求解;
bool isNthPowerOfTwo2(int n){
	if(n<1){
		return false;
	}
	
	return (n&(n-1)) == 0;
}

int main() {
	// your code goes here
	for(int i=0;i<10000;i++){
		if(isNthPowerOfTwo1(i) != isNthPowerOfTwo2(i)){
			cout<<i<<","<<isNthPowerOfTwo1(i)<<","<<isNthPowerOfTwo2(i)<<endl;
			printf("%d,%d,%d\n",i,isNthPowerOfTwo1(i),isNthPowerOfTwo2(i));
		}
	}
	cout<<"yes";
	return 0;
}

 

【程序29】求最长的重复的连续子串

给一个字符串,其各个连续子串中,有重复的子串的最长长度为多少?

#include <iostream>
using namespace std;

int getMaxLengthStrDynamic(char* str, int n, int& maxLength){
    // m[i][j]表示坐标i&j开始的最长相同子串
    // m[n-1][n-1]=1;
    // 1) m[i][n-1]=0或1(初始化完成)
    // 2) m[i][j]=
    //      str[i] == str[j] : m[i+1][j+1] + 1
    //      else : 0
    
    int i,j,k;
    
    if(n<=1) return -1;
    if(str == NULL) return -1;
    
    // 创建m[][]
    int** m = new int*[n];
    for(i=0;i<n;i++){
        m[i]=new int[n];
    }
    
    // 初始化为0
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            m[i][j] = 0;
        }
    }
    
    // 初始化m[i][n-1]
    for(i=0;i<n;i++){
        if(str[i] == str[n-1]){
            m[i][n-1] = 1;
        }else{
            m[i][n-1] = 0;
        }
    }
    
    // 按照公式求解m集合
    for(j=n-2;j>=0;j--){
        for(i=j-1;i>=0;i--){
            if(str[i] == str[j]){
                m[i][j] = m[i+1][j+1] + 1;
            } else {
                m[i][j] = 0;
            }
        }
    }
    
    // 根据m集合寻找最终解
    int maxIndex = -1;
    maxLength = 0;
    for(i=0;i<n;i++){
        for(j=i+1;j<n;j++)
            if(m[i][j] > maxLength){
                maxIndex = i;
                maxLength = m[i][j];
            }
    }
    
    //销毁m
    for(i=0;i<n;i++){
        delete [](m[i]);
    }
    delete []m;
    
    return maxIndex;
}

int getMaxLengthStr(char* str, int n, int& maxLength){
    //int maxLength = 0;
    maxLength = 0;
    int maxIndex = -1;
    int curLength = 0;
    if(n<=1) return -1;
    if(str == NULL) return -1;
    for(int i=0;i<n;i++){
        
        for(int j=i+1;j<n;j++){
            // 当前可能的最大长度为n-j
            if(n-j <= maxLength){
                break;
            }
            
            // 比较当前i和j的最长长度
            curLength = 0;
            for(int t=0;t<n-j;t++){
                // 比较a[i+t] a[j+t]
                if(str[i+t] != str[j+t]){
                    break;
                }
                curLength++;
            }
         if (curLength > maxLength){
                maxLength = curLength;
                maxIndex = i;
            }   
            
            
        }
        
    }
    return maxIndex;
}

int main() {
    //int a;
    //cin >> a;

    char str[23]="fffsabcaccdsseabcaccde";
    //char str[3] = "aa";
    //char str[4] = "aaa";
    //char str[6] = "ababa";
    int maxLength = 0;
    cout << "1:" << endl;
    cout << getMaxLengthStr(str, 22, maxLength) << " " << maxLength << endl;
    cout << "2:" << endl;
    cout << getMaxLengthStrDynamic(str, 22, maxLength) << " " << maxLength << endl;
    
    
    cout << "Hello World!" << endl;
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李小白杂货铺

打赏是一种友谊,让我们更亲密。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值