[2018大华软件创新挑战赛] 初赛1~10题多种思路总结

 [2018大华软件创新挑战赛] 初赛1~10题多种思路总结

赛题以及注意事项(下载):https://files.cnblogs.com/files/usingnamespace-caoliu/%E5%88%9D%E8%B5%9B.rar

 


 

第一题(已优化):

  给定一个正数数组,找出不相邻元素的子序列的和的最大值。如:2、5、3、9应该返回14(5+9);8、5、3、9、1应该返回17(8+9);5 4 10 100 10 5应该返回110(5+100+5);

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 
 4 int max = 0;
 5 
 6 void combination(int* arr,int i,int len)
 7 {
 8     if(i >= len)
 9     {
10         int sum = 0;
11         for(int j=0; j<len-1; j++)
12         {
13             if(arr[j] && arr[j+1]) return;
14             sum += arr[j];
15         }
16 
17         sum += arr[len-1];
18         
19         if(sum > max)
20         {
21             max = sum;
22         }
23         return;
24     }
25 
26     int tmep = arr[i];
27     arr[i] = 0;
28     combination(arr,i+1,len);
29     arr[i] = tmep;
30     combination(arr,i+1,len);
31 }
32 
33 int main()
34 {
35     int t = 0;
36     scanf("%d",&t);
37     while(t--)
38     {
39         int n = 0;
40         scanf("%d",&n);
41         int arr[n];
42         for(int i=0; i<n; i++)
43         {
44             scanf("%d",&arr[i]);
45         }
46         combination(arr,0,n);
47         printf("%d
",max);
48     }
49 }
View Code

思路:与大华模拟题第四题相同,利用递归函数遍历数组中任意位数相加的和,再通过if(arr[j] && arr[j+1]) return;剔除所有包含相邻元素的子序列的和,最后遍历所有符合题意的和,比较后即可得出不相邻元素的子序列的和的最大值。

总结:该方法为通用解法而非最优解,本质为递归嵌套,最终会形成一种类似于二叉树的结构,可通过更改递归最后一层的判定条件来实现绝大部分需遍历的功能。

 


 

第二题(已优化):

  给定一个链表,实现一个函数,将链表中每k个节点进行翻转,若最后一组节点数量不足k个,则按实际个数翻转。例如:给定链表1->2->3->4->5->6->7->8->NULL,k=3,翻转后输出3->2->1->6->5->4->8->7->NULL。
翻转函数reverse有两个参数,分别为链表头指针和翻转步长k。

 1 #include <stdio.h>
 2 
 3 void _release(int* arr,int len)
 4 {
 5     for(int i=0; i<len/2; i++)
 6     {
 7         int t = arr[i];
 8         arr[i] = arr[len-1-i];
 9         arr[len-1-i] = t;
10     }
11 }
12 
13 void release(int* arr,int k,int n)
14 {
15     if(k > n)
16     {
17         _release(arr,n);
18         return;
19     }    
20     _release(arr,k);
21     release(arr+k,k,n-k);
22 }
23 
24 int main()
25 {
26     int t = 0;
27     scanf("%d",&t);
28 
29     while(t--)
30     {
31         int n = 0;
32         scanf("%d",&n);
33         int arr[n];
34         for(int i=0; i<n; i++)
35         {
36             scanf("%d",&arr[i]);
37         }
38 
39         int k = 0;
40         scanf("%d",&k);
41         release(arr,k,n);
42         
43         for(int i=0; i<n; i++)
44         {
45             printf("%d ",arr[i]);
46         }
47         printf("
");
48     }
49 }
View Code(思路1)
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <stdbool.h>
  4 
  5 //#include "linked_list.h"
  6 
  7 typedef int T;
  8 
  9 typedef struct Node
 10 {
 11     //节点保存的数据
 12     T data;
 13     //下一个节点的位置
 14     struct Node* next;
 15 }Node,Link;
 16 
 17 //创建一个链表
 18 Link* creat_link()
 19 {
 20     //创建一个节点,表示头节点,该节点并不保存数据
 21     Link* head = (Link*)malloc(sizeof(Node));
 22     //让头节点的next置NULL,表示链表为空
 23     head->next = NULL;
 24     //返回头的地址
 25     return head;
 26 }
 27 
 28 //清空链表
 29 void clear_link(Link* link)
 30 {
 31     Node* node = link->next;
 32     while(node != NULL)
 33     {
 34         Node* tmp = node;
 35         node = node->next;
 36         free(tmp);
 37     }
 38     link->next = NULL;
 39 }
 40 
 41 //销毁链表
 42 void destroy_link(Link* link)
 43 {
 44     clear_link(link);
 45     free(link);
 46     link = NULL;
 47 }
 48 
 49 //判断链表是否为空
 50 bool emtpy_link(Link* link)
 51 {
 52     return !link->next;
 53 }
 54 
 55 //获得链表里元素的个数
 56 size_t size_link(Link* link)
 57 {    
 58     size_t i = 0;
 59     //用node记录头节点的下一个位置
 60     Node* node = link->next;
 61     //只要node不为NULL,表示该节点存在
 62     while(node != NULL)
 63     {
 64         //让node继续指向下一个节点
 65         node = node->next;
 66         i++;
 67     }
 68     return i;
 69 }
 70 
 71 //返回下标为index的前一个节点
 72 Node* getNode_link(Link* link, int index)
 73 {
 74     Node* node = link;
 75     //如果index=0 其前一个节点就为link ,并不会进入下面循环
 76     for(int i=0; node != NULL && i<index; i++)
 77     {
 78         node = node->next;
 79     }
 80     return node;
 81 }
 82 
 83 //更新链表下标为index的节点的值
 84 bool update_link(Link* link, int index, T value)
 85 {
 86     if(index < 0 || index > size_link(link)-1)
 87         return false;
 88     Node* node = getNode_link(link, index+1);
 89     node->data = value;
 90     return true;
 91 }
 92 
 93 //插入一个元素到指定位置 index取值范围[0,size_link(Link* link)]
 94 bool insert_link(Link* link, int index, T value)
 95 {
 96     if(index < 0 || index > size_link(link)) 
 97         return false;
 98     //得到下标为index位置的前一个节点
 99     Node* prevNode = getNode_link(link, index);
100     //申请内存,用于保存需要插入的数据
101     Node* node = (Node*)malloc(sizeof(Node));
102     node->data = value;
103     //插入节点的下一个节点指向index前一个节点的后节点
104     node->next = prevNode->next;
105     //让index的前节点的下一个节点指向当前插入的节点
106     prevNode->next = node;
107     return true;
108 }
109 //插入一个元素到链表末尾
110 bool insertBack_link(Link* link, T value)
111 {
112     return insert_link(link, size_link(link), value);
113 }
114 //插入一个元素到链表首部
115 bool insertFront_link(Link* link, T value)
116 {
117     return insert_link(link, 0, value);
118 }
119 
120 //删除指定下标的元素
121 bool delete_link(Link* link, int index)
122 {
123     if(index < 0 || index > size_link(link)-1) 
124         return false;
125     //获得需要删除节点的前一个节点
126     Node* prevNode = getNode_link(link, index);
127     //保存要删除的节点,用于释放内存
128     Node* node  = prevNode->next;
129     //让要删除节点的前一个节点指向要删除节点的后一个节点
130     prevNode->next = prevNode->next->next;
131     free(node);
132     return true;
133 }
134 
135 //删除元素为value的所有节点,返回值表示该链表的数据是否发生变化
136 bool deleteDatas_link(Link* link, T value)
137 {
138     //作为是否删除成功的一个标志
139     bool flag = false;
140     Node* node = link->next;
141     for(int i = 0; node != NULL; i++)
142     {
143         if(node->data == value)
144         {
145             delete_link(link, i);
146             //删除后判定下标前移
147             i--;
148             flag = true;
149         }
150         node = node->next;
151     }
152     return flag;
153 }
154 
155 //删除元素为value的第一个元素
156 bool deleteData_link(Link* link, T value)
157 {
158     Node* node = link->next;
159     for(int i = 0; node != NULL; i++)
160     {
161         if(node->data == value)
162             //作为是否删除成功的一个标志
163             return delete_link(link, i);
164         node = node->next;
165     }
166     return false;
167 }
168 
169 //查找元素value的下标
170 int indexOf_link(Link* link, T value)
171 {
172     Node* node = link->next;
173     for(int i = 0; node != NULL; i++)
174     {
175         if(node->data == value)
176             return i;
177         node = node->next;
178     }
179     return -1;
180 }
181 
182 //遍历链表
183 void travel_link(Link* link)
184 {
185     Node* node = link->next;
186     while(node != NULL)
187     {
188         printf("%d ",node->data);
189         node = node->next;
190     }
191     puts("");
192 }
193 
194 //链表逆序
195 void reverse(Link link)
196 {
197     if(link == NULL || link->next == NULL)
198         return;
199     //0、记录前一个节点与当前节点
200     Node* prevNode = link->next;
201     Node* node = prevNode->next;//NULL
202     //只要当前节点存在
203     while(node != NULL)
204     {
205         //1、先记录当前节点的后一个节点
206         Node* nextNode = node->next;
207         //2、让当前节点(node)的下一个节点(node->next)指向(=)前一个节点(prev)
208         node->next = prevNode;
209         //3、让前一个节点指向当前节点、当前节点指向原先记录的下一个节点
210         prevNode = node;
211         node = nextNode;
212     }
213     //4、让原来的第一个元素变为尾元素,尾元素的下一个置NULL
214     link->next->next = NULL;
215     //5、让链表的头节点指向原来的尾元素
216     link->next = prevNode;
217 }
218 
219 //链表中每k个节点进行翻转,若最后一组节点数量不足k个,则按实际个数翻转。
220 void reverseByNum(Node* prev,Node* node,int num)
221 {
222     if(node == NULL)
223         return;
224     Node* prevNode = node;
225     Node* curNode = node->next;
226     int count = 1;
227     while(curNode != NULL)
228     {
229         Node* nextNode = curNode->next;
230         curNode->next = prevNode;
231         prevNode = curNode;
232         curNode = nextNode;
233         count++;
234         if(count == num)
235         {
236             Node* tmp = prev->next;
237             prev->next->next = curNode;
238             prev->next = prevNode;
239             reverseByNum(tmp,curNode,num);
240             return;
241         }
242     }
243     prev->next->next = curNode;
244     prev->next = prevNode;
245 }
246 
247 void reverses(Link link,int num)
248 {
249     Node *node = link->next;    
250     reverseByNum(link,node,num);
251 }
View Code(思路2)

思路1:该题可以通过数组来实现要求,按照题意将数组分割并分别翻转即可。同时要考虑到数组无法均分的情况。

思路2:通过链表实现,详见博客第三章节:[C语言]单向链表的构建以及翻转算法

总结:该题很简单,留意数组无法均分的情况即可。

 


 

第三题(已优化):

输入一个由大写字母‘A-Z’组成的字符串,按照如下方式,对字符串中的字符进行编码:
1.统计每个字符出现的次数,并根据出现次数对字符进行权重分配;
a.出现次数越高,权重越大; b.出现次数相同,字符值越小,权重越大; c.权重值为1~26,必须从1开始,连续分配,数值越大,权重越高;
2.把权重最小的两个字符构成二叉树的左右子节点,其和作为根节点(节点值为字符权重);
a.若两个子节点值不等,较小值作为左子节点;
b.若两个子节点值相同(多字符的权重和与另一个字符权重可能相同),则叶子节点作为左子节点;
3.重复过程2,直到根节点值为所有字符的权重和;
4.将树的左边定位0,右边定位1,从根节点到字符节点的路径,即为其编码值;
示例:
1.输入字符串‘MBCEMAMCBA’,各字符出现次数分别为M:3,A:2,B:2,C:2,E:1;
2.根据字符出现次数和字符大小,进行排序,结果为:M>A>B>C>E,对应权重值为 M:5,A:4,B:3,C:2,E:1
3.根据规则生成权重树:
a.(M:5,A:4,B:3,C:2,E:1)中E:1和C:2值最小,相加得3,则E:1为左节点,C:2为右节点,3为根节点;
b.(M:5,A:4,B:3,3)中B:3和3值最小,相加得6,则B:3为左节点,3为右节点,6为根节点;
c.(M:5,A:4,6)中M:5和A:4值最小,相加得9,则A:4为左节点,M:5为右节点,9为根节点;
d.(6,9)中只有两个值,相加得15,则6为左节点,9为右节点,15为根节点;
e.根节点达到所有字符的权重和1+2+3+4+5=15,结果如下:
               15
           0/     1
         6            9
      0/ 1       0/ 1
   B:3      3   A:4   M:5
           0/ 1
         E:1   C:2

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 typedef struct Node
  5 {
  6     char ch;
  7     char code;
  8     int count;
  9     int weight;
 10     struct Node* left;
 11     struct Node* right;
 12 }Node;
 13 
 14 Node* create_node(void)
 15 {
 16     Node* node = malloc(sizeof(Node));
 17     node->ch = 0;
 18     node->weight = 0;
 19     node->left = NULL;
 20     node->right = NULL;
 21 }
 22 
 23 int ch_cnt = 0;
 24 char arr_code[10000] = {};
 25 Node arr_node[26] = {};
 26 
 27 void count_weight(char* str)
 28 {
 29     for(int i=0,j=0; str[i]; i++)
 30     {
 31         for(j=0; j<ch_cnt; j++)
 32         {
 33             if(str[i] == arr_node[j].ch) break;
 34         }
 35 
 36         if(j<ch_cnt) continue;
 37         arr_node[ch_cnt++].ch = str[i];
 38     }
 39 
 40     for(int i=0; i<ch_cnt; i++)
 41     {
 42         for(int j=0; str[j]; j++)
 43         {
 44             if(arr_node[i].ch == str[j]) arr_node[i].count++;
 45         }
 46     }
 47 
 48     for(int i=0; i<ch_cnt-1; i++)
 49     {
 50         for(int j=i+1; j<ch_cnt; j++)
 51         {
 52             if(arr_node[i].count > arr_node[j].count ||
 53                 (arr_node[i].count==arr_node[j].count && arr_node[i].ch < arr_node[j].ch))
 54             {
 55                 Node t = arr_node[i];
 56                 arr_node[i] = arr_node[j];
 57                 arr_node[j] = t;
 58             }
 59         }
 60     }
 61 
 62     for(int i=0; i<ch_cnt; i++)
 63     {
 64         arr_node[i].weight = i+1;
 65     }
 66 }
 67 
 68 void create_tree(void)
 69 {
 70     if(ch_cnt<=1) return;
 71 
 72     Node* root = create_node();
 73     root->left = create_node();
 74     root->right = create_node();
 75 
 76     *(root->left) = arr_node[0];
 77     *(root->right) = arr_node[1];
 78 
 79     root->left->code = '0';
 80     root->right->code = '1';
 81 
 82     root->weight += root->left->weight;
 83     root->weight += root->right->weight;
 84     
 85 
 86     arr_node[1] = *root;
 87 
 88     for(int i=0; i<ch_cnt-1; i++)
 89     {
 90         arr_node[i] = arr_node[i+1];
 91     }
 92 
 93     ch_cnt--;
 94 
 95     for(int i=0; i<ch_cnt-1; i++)
 96     {
 97         for(int j=i+1; j<ch_cnt; j++)
 98         {
 99             if(arr_node[i].weight > arr_node[j].weight ||
100                 (arr_node[i].weight == arr_node[j].weight&&!arr_node[i].ch))
101             {
102                 Node t = arr_node[i];
103                 arr_node[i] = arr_node[j];
104                 arr_node[j] = t;
105             }
106         }
107     }
108     create_tree();
109 }
110 void show_tree(Node* tree)
111 {
112     if(NULL == tree) return;
113     show_tree(tree->left);
114     show_tree(tree->right);
115 }
116 
117 int find_tree(Node* tree,char ch)
118 {
119     if(NULL == tree) return 0;
120     if(tree->ch == ch)
121     {
122         arr_code[ch_cnt++] = tree->code;
123         return 1;
124     }
125 
126     if(find_tree(tree->left,ch) || find_tree(tree->right,ch))
127     {
128         arr_code[ch_cnt++] = tree->code;
129     }
130 }
131 
132 int main()
133 {
134     int t = 0;
135     scanf("%d",&t);
136     char str[t][1000];
137     for(int i=0; i<t; i++)
138     {
139         gets(str[i]);
140     }
141     for(int i=0; i<t; i++)
142     {
143         count_weight(str[i]);
144         create_tree();
145         for(int j=0; str[i][j]; j++)
146         {
147             ch_cnt = 0;
148             find_tree(arr_node,str[i][j]);
149             for(int k=ch_cnt-1; k>=0; k--)
150             {
151                 printf("%c",arr_code[k]);
152             }
153         }
154     }
155 
156 }
View Code(思路二)

方向:此题与哈夫曼树相关(哈夫曼树:https://blog.csdn.net/move_now/article/details/53398753)

思路一:以哈夫曼树为主体,在此基础上添加对字符串中各个字符分配权重的功能,最后分解字符串,根据各个字符的权重依次输出路径。

思路二:以每类字符为主体,创建储存各类字符信息的结构体,计算出各个字符的权重,延伸出两个空子叶,在此基础上根据各个字符的权重、按照题目规则将其连接成哈夫曼树形结构,最后分解字符串,根据各个字符的权重依次输出路径。

总结:总的来说,思路一是在现有哈夫曼树框架下进行代码的修改、添加,轮子已经造好,我们直接拿来用即可,能极大地减少实际代码量,但修改现有哈夫曼树代码时容易打乱自身思路且缺少创造性;思路二是以每类字符为主体,能在编写其结构体时有效的整理思路,整个程序关联性强,富有创造力,但程序难度较大。

PS :该程序本地测试无误,但上传后一直为0分,未能查明原因。

 


 

第四题(未优化):

  实现find_sub_string(str1,str2)函数,判断字符串str2在str1中出现的次数。返回str2在str1中出现的次数。

int find_sub_string(const std::string& str1, const string& str2);

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 int find_sub_string(char* str1, char* str2)
 5 {
 6     int count = 0,i2 = 0;  
 7     if(*str2 && *str1)  
 8     { 
 9             for(int i=0; *(str1+strlen(str2)+i-1); i++)  
10             {
11                     for(int j=i; *(str1+j); j++)  
12             {
13                             if(*(str1+j) == *(str2+i2)) 
14                         {  
15                                if(strlen(str2)-1 == i2) 
16                     {
17                         count++; 
18                         i2 = 0; 
19                         break;
20                     }
21                     else i2++;
22                         } 
23                 else 
24                 {
25                     i2 = 0; 
26                     break;
27                 }
28             }
29             }
30     }  
31     else  return count;  
32 }
33 
34 int main()
35 {
36     int t;
37     scanf("%d",&t);
38     int x = t; 
39     int a[t];
40 
41     while(x--)
42     {
43         char str1[10][1000] = {};
44         char str2[10][1000] = {};
45         scanf("%s",str1[x]);
46         scanf("%s",str2[x]);
47         printf("%d
",find_sub_string(str1[x], str2[x]));
48     }    
49 }        
View Code

思路:思路简单,循环嵌套。

总结:该题可利用库函数实现。

 


 

第五题:

有一个吃金币游戏:
1.地图中有N个城堡(编号分别为0~N-1),每个城堡中挂着一面旗子;
2.某些城堡之间是连通的,在其连通的路上,分散着若干个金币(个数可能相同,也可能不同);
3.玩家走过存在金币的路之后,可以获得这条路上的所有金币,11同一条路走多次,只有第一次可获得金币;

游戏规则:
1.玩家可以从任意一个城堡出发;
2.玩家必须拿到所有城堡的旗子;
3.玩家走过的所有路,不能存在环路;
4.一定存在至少一条能让玩家拿到所有旗子的路线;

请设计一个算法,计算在遵守以上游戏规则的的前提下,最多能获取到的金币个数。

补充规则中对环路的说明:
1、环路就是最终走过的路径中存在圈,实例如下:
非环路: 环路:1-2-5-4-1则成为环路
1----2 3 1----2----3
| | | |
| | | |
4----5----6 4----5----6
| | | | |
| | | | |
7 8 9 7 8----9

2、如说明1中的路径,玩家走1-2-5-4-7-4-5,虽然无圈,但是4-7、5-4中的路径金币只能吃一次,重复走第二次金币不可以获取

 


 

第六题(未优化):

  在H.264视频编码标准中,编码帧由NALU头和NALU主体组成,其中NALU头由一个字节组成。在实际编码时,在每个NAL前添加起始码 0x000001,解码器在码流中检测到起始码,当前NAL结束。
  为了防止NAL内部出现0x000001的数据,在编码完一个NAL时,如果检测出有连续两个0x00字节,就在后面插入一个0x03。
  当解码器在NAL内部检测到0x000003的数据,就把0x03抛弃,恢复原始数据。给定一组包含SPS NALU的编码数据,找出解码后的SPS数据。比如:
  输入:{0x1f 0x00 0x00 0x01 0x67 0x42 0xd0 0x80 0x00 0x00 0x03 0x00 0x80 0x00 0x0f 0x00 0x00 0x01 0x68 0xce 0x3c 0x80},
  处理:在上述例子中,第一个0x00 0x00 0x01为识别到的数据头,0x00 0x00 0x03 0x00数据中,0x03为需抛弃数据,第二个0x00 0x00 0x01为下一个数据头,那么取出两个数据头中间的数据并且抛弃掉此0x03,结果如下:{0x67 0x42 0xd0 0x80 0x00 0x00 0x00 0x80 0x00 0x0f }。

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 void find_sps_in_h264(unsigned int* num, int len)
 5 {
 6     unsigned int n[50] = {};
 7     for(int i=2; i<len; i++)
 8     {
 9         if(num[i-2]==0x00 && num[i-1]==0x00 && num[i]== 0x01)
10         {
11             for(int j=2; i<len-1; j++)
12             {
13                 n[j] = num[++i];
14                 if(j>=4 && n[j-2]==0x00 && n[j-1]==0x00 && n[j]== 0x01)
15                 {
16                     for(int k=2; k<j-2; k++)
17                     {
18                         if(k>=4 && n[k-2]==0x00 && n[k-1]==0x00 && n[k]== 0x03)
19                         {
20                             continue;
21                         }
22                         else printf("%x ",n[k]);
23                     }
24                     puts("");
25                     return;
26                 }
27                 else if(i == len-1)
28                 {
29                     for(int k=2; k<=j; k++)
30                     {
31                         if(k>=4 && n[k-2]==0x00 && n[k-1]==0x00 && n[k]== 0x03)
32                         {
33                             continue;
34                         }
35                         else printf("%x ",n[k]);
36                     }
37                     puts("");
38                     return;
39                 }
40             }
41         }
42     }
43 }
44 
45 int main()
46 {
47     int t;
48     scanf("%d",&t);
49 
50     int len;
51     unsigned int num[51] = {};
52     for(int i=0; i<t; i++) 
53     {
54         scanf("%d",&len);
55         if(len<7) len = 7;
56         else if(len>50) len =50;
57         for (int j=0; j<len; j++)
58         {
59             scanf("%x",&num[j]);
60         }
61         find_sps_in_h264(num,len);
62     }
63     return 0;
64 }
View Code

思路:通过识别编码“0x00 0x00 0x01”从码流中取出两个数据头中间的数据,再将数据中“0x00 0x00 0x03”中的编码“0x03”去除。

总结:可以将需要重复使用的功能模块进行函数封装,优化代码,提高代码简洁性与可阅读性。如:判定数据头的功能模块。

 


 

第七题(已优化):

  给定一个全小写的英文字符串,请在该字符串中找到一个连续子字符串,使得子字符串没有重复的字符并且长度最长,计算此最长字符串的长度。比如:abcbdeab,最长的子字符串为cbdea,长度为5;aaaa,最长子字串为a,长度为1。

 1 #include <stdio.h>
 2 
 3 int longest_substr(char* str)
 4 {
 5     int longest = 0;
 6     for(int i=0,j=0; str[i]; i++)
 7     {
 8         for(j=i+1; str[j]; j++)
 9         {
10             int flag = 0;
11             for(int k=j-1; k>=i; k--)
12             {
13                 if(str[j] == str[k]) flag = 1;
14             }
15 
16             if(flag) break;
17         }
18         int len = j - i;
19         if(len > longest) longest = len;
20     }
21     return longest;
22 }
23 
24 int main()
25 {
26     int n = 0;
27     scanf("%d",&n);
28     char str[n][101];
29     
30     for(int i=0; i<n; i++)
31     {
32         stdin->_IO_read_ptr = stdin->_IO_read_end;
33         gets(str[i]);
34     }
35 
36     for(int i=0; i<n; i++)
37     {
38         printf("%d
",longest_substr(str[i]));
39     }
40 }
View Code

思路:思路简单,循环嵌套。

总结:三层for循环嵌套之间的关系要理清。

 


 

第八题(已优化):

  一个数可以用二进制表示,也可以用十进制表示,如果该数的二进制表示法所有位数字之和等于十进制表示法所有位数字之和,则称该数为神奇数。比如:21(十进制)=10101(二进制),所有位数之和为2+1=3,1+0+1+0+1=3。求小于等于M的神奇数有多少个。

 1 #include <stdio.h>
 2 
 3 int is_magical(int num)
 4 {
 5     int sum1 = 0;
 6     for(int i=0; i<32; i++)
 7     {
 8         sum1 += (num>>i)&1;
 9     }
10 
11     int sum2 = 0;
12     while(num)
13     {
14         sum2 += num%10;
15         num /= 10;
16     }
17 
18     return sum1 == sum2;
19 }
20 
21 int main()
22 {
23     int cnt = 0;
24     scanf("%d",&cnt);
25     int m[cnt];
26 
27     for(int i=0; i<cnt; i++)
28     {
29         scanf("%d",&m[i]);
30     }
31 
32     for(int i=0; i<cnt; i++)
33     {
34         int n = 0;
35         for(int j=1; j<=m[i]; j++)
36         {
37             is_magical(j) && n++;
38         }
39         printf("%d
",n);
40     }
41 }
View Code

思路:思路简单,考点主要在于对二进制数据的应用。

总结:二进制所有位数之和可使用用语句 for(int i=0; i<32; i++)  sum1 += (num>>i)&1; 计算。

 

 


 

第九题(未优化):

  我们来玩一个大富翁的游戏,在地图上有N个连续的城市,城市对某件商品的售价为[V1,V2,V3...VN],你作为未来的大富翁看到其中的商机,打起了倒卖商品赚取差价的主意。约束条件:你只能顺序从第一个城市出发,不能走回头路,每次只能交易一件商品,再下次买入商品后必须卖掉之前买入的商品,求你能赚取的最大财富。比如:城市商品售价为[1,9,2,3,8],最大财富为(9-1)+(8-2)=14;城市商品售价为[9,8,3,2,1],最大财富为0,因为你买啥都无法赚钱。

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 int min;
 5 int max;
 6 int a;
 7 
 8 int x(int* val)
 9 {
10     int sum = 0;
11     int i = 0;
12     int m = max, n = min;
13     while(val[i])
14     {
15         if(val[i] < val[i+1])
16         {
17             min = i++;
18             while(val[i] < val[i+1] && val[i])
19             {
20                 i++;
21             }
22                 max = i++;
23                 sum += val[max]-val[min];
24         }
25         else i++;
26     }
27     return sum;
28 }
29 
30 int main()
31 {
32     int t;
33     scanf("%d",&t);
34 
35     int val[10][100] = {};
36     for(int i=0; i<t; i++) 
37     {
38         scanf("%d",&a);
39         for (int j=0; j<a; j++)
40         {
41             scanf("%d",&val[i][j]);
42         }
43     }
44     for(int i=0; i<t; i++)
45     {
46         printf("%d
",x(val[i]));
47     }    
48 }
View Code(思路二)

思路一:直接比较当前城市售价与下一城市售价,若下一城市售价高于当前城市售价,则对二者的差值进行累加,遍历城市后即可获得最大财富。

思路二:寻找不同城市售价的“拐点”,遍历城市将所有彼此邻近的“最高拐点”与“最低拐点”的差值进行累加,即可获得最大财富。

总结:总的来说,思路一简单粗暴,代码简单、极易实现,但相比思路二会进行更多的加法运算;思路二理论上算法更优,但在该题中无法体现其优势。

 


 

第十题(已优化):

假设你站在一棵二叉树的右边,从上往下,请输出你看到的节点。比如:
       5 <---
      /
    2    3 <---
  /      /
4      6    8 <---
返回 538。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 static int index = 0;
 5 
 6 typedef struct Node
 7 {
 8     char ch;
 9     struct Node* left;
10     struct Node* right;
11 }Node;
12 
13 Node* create_node(char ch)
14 {
15     Node* node = malloc(sizeof(Node));
16     node->ch = ch;
17     node->left = NULL;
18     node->right = NULL;
19 }
20 
21 void create_tree(Node** tree,char* str)
22 {
23     if(''==str[index]) return;
24     
25     if(str[index] == '#')
26     {
27         *tree = NULL;
28         return;
29     }
30     *tree = create_node(str[index]);
31     if(str[index+1])
32     {
33         index++;
34         create_tree(&(*tree)->left,str);
35     }
36 
37     if(str[index+1])
38     {
39         index++;
40         create_tree(&(*tree)->right,str);
41     }
42 }
43 
44 void show_tree(Node* tree)
45 {
46     if(NULL == tree) return;
47     printf("%c
",tree->ch);
48     show_tree(tree->right);
49 }
50 
51 int main()
52 {
53     int t = 0;
54     scanf("%d",&t);
55     char str[t][1000];
56     for(int i=0; i<t; i++)
57     {
58         gets(str[i]);
59     }
60     for(int i=0; i<t; i++)
61     {
62         index = 0;
63         Node* tree = NULL;
64         create_tree(&tree,str[i]);
65         show_tree(tree);
66     }
67 }
View Code

思路:思路简单,考点主要在于构建二叉树。

总结:该题有一个小坑,多心的解题人可能会考虑到:若上图中的6存在子叶x,那么“从上往下、从右往左”看的时候是否会“看”到x?若将这种可能性考虑进去,代码的复杂程度提高数十倍的同时会导致该题考察点不明确,考察者没有这么做的意义。实际上这种可能性不属于该题的考察点,考虑过多反而容易误解题意。

 


 

实现效果:

 

原文地址:https://www.cnblogs.com/usingnamespace-caoliu/p/9027122.html