C语言程序设计100例之(43):Excel地址
例43 Excel地址
问题描述
Excel是常用的办公软件。在Excel表格中,每个单元格都有唯一的地址表示。比如:第12行第4列表示为“D12”,第5行第255列表示为“IU5”。
事实上,Excel提供了两种地址表示方法,还有一种表示法叫做RC格式地址。 第12行第4列表示为“R12C4”,第5行第255列表示为“R5C255”。
编写程序,实现从RC地址格式到常规地址格式的转换。
输入格式
第1行是一个正整数n(n<100),表示接下来有n行输入数据。
接着输入的n行数据是RC格式的Excel单元格地址表示法。
输出格式
输出n行数据,每行是转换后的常规地址表示法。
输入样例
R12C4
R5C255
输出样例
D12
IU5
(1)编程思路。
首先将输入的RC格式地址中的行和列的值求出来。例如,输入R5C255后,可求出行值row=5,列值col=255。
之后将列值col转换成26进制表示的数,方法为将col不断除以26,记下余数,直到商为0,每个余数保存到数组d中。例如,255转换为26进制数是一个两位数,高位为9(保存到d[1]中),低位为21(保存到d[0])中。然后将数字映射为字母,1对应A,2对应B,…,9对应I,…,21对应U,…,26对应Z。这样,255列表示为IU。
但在具体处理时,由于1~26表示A~Z,因此得到的余数若为0,则应对应为26,即26不进位,由此需向前借1位。例如,列值col=52时,转换为26进制数码d[1]=2,d[0]=0,此时由于d[0]<=0,d[0]=d[0]+26=26,d[1]=d[1]-1=1,再对应表示为AZ。
(2)源程序。
#include
#include
int main()
{
int n;
scanf("%d",&n);
while (n--)
{
char str[24],colstr[9];
int row,col,d[7],i,k,len,flag;
scanf("%s",str);
row=0; col=0; flag=0;
for (i=1;str[i]!='\0';i++)
{
if (str[i]!='C')
{
if (flag==0)
row=row*10+str[i]-'0';
else
col=col*10+str[i]-'0';
}
else
flag=1;
}
len=0;
while (col!=0)
{
d[len++]=col%26;
col/=26;
}
for (i=0;i if (d[i]<=0) { d[i]=d[i]+26; d[i+1]--; } if (d[len-1]==0) len--; k=0; for (i=len-1;i>=0;i--) colstr[k++]=d[i]-1+'A'; colstr[k]='\0'; printf("%s%d\n",colstr,row); } return 0; } 本题选自POJ题库 (http://poj.org/problem?id=1715) Description The base of the hexadecimal system is 16. In order to write down numbers in this system one uses 16 digits 0,1,...,9,A,B,C,D,E,F. The capital letters A,..,F stands for 10,..,15, respectively. For instance the value of the hexadecimal number CF2 is 12 * 162 + 15 * 16 + 2 = 3314 in the decimal system. Let X be the set of all positive integers whose hexadecimal representations have at most 8 digits and do not contain repetitions of any digit. The most significant digit in such a representation is not zero. The largest element in X is the number with the hexadecimal representation FEDCBA98, the second largest is the number FEDCBA97, the third is FEDCBA96 and so forth. Write a program that: finds the n-th largest element of X; Input The first line of the file standard input contains integer n in the decimal representation. n does not exceed the number of elements of X. Output Your program should output the n-th largest element in X in the hexadecimal representation. Sample Input 11 Sample Output FEDCBA87 (1)编程思路。 本题是给定16进制数的16个数码,求第k大的数,要求数的长度最大为8,并且每个数码互不相同。 第1大的数为FEDCBA98,第2大的数为FEDCBA97,第2大的数为FEDCBA96,…,倒数第2大的数为1,最小的数为0。 先确定第k大的数的位数,最多为8位。8位数一共有15*15*14*13*12*11*10*9=486486000个,最高位不能为0,在1~F这15个数码中任取一个作为最高位,第2位在剩下的15个数码中任取1位,第3位在剩下的14个数码中任取1位,…,第8位在剩下的9个数码中任取1位,所以8位数的个数为486486000个。同理,7位数的个数为15*15*14*13*12*11*10=54054000个;6位数的个数为15*15*14*13*12*11=5405400个;5位数的个数为15*15*14*13*12=491400个;4位数的个数为15*15*14*13=40950个;3位数的个数为15*15*14=3150个;2位数的个数为15*15=225个;1位数的个数为15个(指非0,若包括0,则有16个)。上述所有数加起来为546481140,即第546481141大的数为0。 定义数组int m[10]={0,15,225,3150,40950,491400,5405400,54054000,486486000};保存各长度数的个数。由上面分析知,第486486000大的数是最小的8位数10234567,第486486001大的数应该就是最大的7位数FEDCBA9;第486486000+54054000=540540000大的数一定是最小的7位数1023456,第540540001大的数一定是最大的6位数FEDCBA。由此可以确定第k大的数的位数,若k<=486486000,则位数为8,同时可确定也是第k大的8位数;若486486000 确定第k大的数也是第n大的len位数后,再从高位往低位进行处理。若最高位确定为F,则后7位一共有15*14*13*12*11*10*9,简记为mul(15,7),表示从15~9这连续的7个数相乘,也就是说,若k<=mul(15,7),则第k大的8位数最高位一定为F。可以验证第mul(15,7)=32432400大的数一定为F0123456,则第32432401大的数一定为EFDCBA98。若mul(15,7) 确定了最高位后,再确定次高位。此时,需要将k%mul(15,7),不妨设结果为k1。若确定次高位是剩下的15个数码中的最大,则后面6位一共可表示14*13*12*11*10*9=2162160,简记为mul(14,6),也就是说若k1<=mul(14,6),则第k大的数的次高位一定是剩下15个数码中最大的,若mul(14,6) 同理,可确定之后的各位。 (2)源程序。 #include int mul(int x,int y) { int p=1; while (y--) { p*=x; x--; } return p; } int main() { int m[10]={0,15,225,3150,40950,491400,5405400,54054000,486486000}; char num[17]="0123456789ABCDEF"; int b[10],vis[20]; int n; while (scanf("%d",&n)!=EOF) { if (n>=546481141) { printf("0\n"); continue; } int len=8; while (n>m[len]) { n-=m[len]; len--; } n--; int i,j; for (i=len;i>=1;i--) { int t=mul(15+i-len,i-1); b[i]=n/t; n%=t; } for (i=1;i<=16;i++) vis[i]=0; for (i=len;i>=1;i--) { int s=0; for (j=1;j<=16;j++) { if (vis[j]==0) { s++; if (s==b[i]+1) { vis[j]=1; break; } } } printf("%c",num[16-j]); } printf("\n"); } return 0; } 问题描述 某系统中给每件物品编制一个唯一的识别码,该识别码由26个小写字母中最多50个字符组成。任何给定代码的字符集均可随意选择。但一旦选择了一组字母,在更改该组字母之前,将使用从该组字母派生的所有可能代码作为识别码。 例如,假设选定的字符集包含3个“a”、2个“b”和1个“c”,那么由这组字母编制的识别码按字母顺序依次如下: aaabbc aaabcb …… abaabc abaacb ababac …… cbbaaa 编写一个程序来帮助发布这些识别码。输入一个不超过50个小写字母(可能包含重复字符)组成的字符串,输出其下一个排列字符串。 输入 输入由一系列行组成,每行包含一个表示给定代码的字符串。整个文件将以一行结尾,该行由一个#组成。 输出 求输入字符串的下一个排列串。若不存在,就输出No Successor。 输入样例 abaacb cbbaa 输出样例 ababac No Successor (1)编程思路。 求输入字符串的下一个排列字符串的方法如下,为描述方便,设字符串中字符记为1~n。 设P是1~n的一个全排列:p=p1p2……pn =p1p2……pj-1pjpj+1……pk-1pkpk+1……pn 1)从排列的右端开始。找出第一个比右边数字小的数字的序号j(j从左端开始计算)。即 j=max { i | pi < pi+ 1}。 2)在pj的右边的数字中。找出全部比pj大的数中最小的数字pk,即 k=max{i|pi>pj}。由于右边的数从右至左是递增的,因此k是全部大于pj的数字中序号最大者。 3)交换pj,pk。 4)再将pj+1……pk-1pkpk+1……pn倒转得到排列 p’=p1p2…..pj-1pjpn…..pk+1pkpk-1…..pj+1。 这就是排列p的下一个排列。 例如,设有排列:1,3,5,4,2,其中5-4-2即为递增序列。找到a[1]=3;从上述序列中找一个比a[1](3)大的最小数(4),并将且交换这两个数。于是1,3,5,4,2变换为1,4,5,3,2,再将4后面的数字递增排列,即1,4,2,3,5。 因此,得到1,3,5,4,2的下一个排列为1,4,2,3,5。 (2)源程序1。 #include #include int theNext(char a[],int n) { int len =n-1,front; int flag=0; while (len>0) {习题43
43-1 Hexadecimal Numbers
43-2 识别码