1.请设计一个算法,给一个字符串进行二进制编码,使得编码后字符串的长度最短。
输入描述:
每组数据一行,为待编码的字符串。保证字符串长度小于等于1000。
输出描述:
一行输出最短的编码后长度。
MT-TECH-TEAM
输出
33
解析:哈夫曼编码。先统计各字符次数,按照从小到大排序。初始化每个字符编码长度为0,然后每次找到次数最少的两个结点,合并为一个结点,新结点的次数为两个结点的和,直到最后只剩下一个结点,计算各个字符的编码长度(即在树中的深度),最后,对原始字符串每个字符的编码长度求和。(其实就是找到最小的两个数,求累加和,结果就是哈夫曼权值)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
char str[1005];
while (scanf_s("%s", str,2005) != 0)
{
int tmp[255] = { 0 };
int len = strlen(str);
for (int i = 0; i < len; i++)
tmp[str[i]]++;
vector<int> vec;
for (int i = 0; i < 255; i++)
if (tmp[i] != 0)
vec.push_back(tmp[i]);
int sum = 0;
while (vec.size() > 1)
{
sort(vec.begin(), vec.end());
int temp = vec[0] + vec[1];
sum += temp;
vec.erase(vec.begin());
vec.erase(vec.begin());
vec.push_back(temp);
}
printf("%d\n",sum);
}
system("pause");
return 0;
}
2.对于一个由0..n的所有数按升序组成的序列,我们要进行一些筛选,每次我们取当前所有数字中从小到大的第奇数位个的数,并将其丢弃。重复这一过程直到最后剩下一个数。请求出最后剩下的数字。
输入描述:
每组数据一行一个数字,为题目中的n(n小于等于1000)。
输出描述:
一行输出最后剩下的数字。
输入
500
输出
255
解析:一共n+1个数,从数组0位置开始,第0个位置的元素为第一个数,第i个位置的元素为底i+1个数。设置两个下标 i、j,i 记录现在访问的数组下标,j 每层循环都从0开始,i 每遍历到偶数个位置时,位置 j 处就把位置 i 处的元素记录下来,i每次步长为2,i 第一次遍历到位置 n处,以后每次都遍历到上次 j的最大位置,直到 j 最大为0是结束。
#include<stdio.h>
int arr[1001];
int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
for (int i = 0; i <=n; i++)
arr[i] = i;
int i = 0, j, flag = n;
while (flag != 0)
{
j = 0;
i = 0;
for (; i <= n; i += 2)
{
arr[j++] = arr[i + 1];
}
n = j - 1;
flag = j - 1;
}
printf("%d\n", arr[0]);
}
return 0;
}
3.有一个二维数组(n*n),写程序实现从右上角到左下角沿主对角线方向打印。
给定一个二位数组arr及题目中的参数n,请返回结果数组。
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]],4
返回:[4,3,8,2,7,12,1,6,11,16,5,10,15,9,14,13]
解析:找规律。从数组右上角开始,第一个数为最右上的元素,第二个为第一行倒数第二个元素,设此时元素的行列坐标位置分别为 i、就,之后,沿主对角线打印,即下一个元素为i++,j++;只需考虑 j=n-1 和 i=n-1的情况,其他情况都可以用(i++,j++)进行打印下一个元素。当j=n-1时,换行,换到第一行即i=0,j位置为上一次i=0的时候的j的位置向左移一位;当j的位置左移到最左端时,即arr[0][0]位置,下次在换行时,从i=1,j=0开始,再次换行是从i=2,j=0开始,以此类推;当i=n-1时,换行,和最后一次因j=n-1而换行后的位置向下移一位(记此位置为pos),之后再换行时,从pos向下移一位的位置开始,同时pos更新,依次类推,直到最后一个元素arr[n-1][0]打印后结束。
vector<int> arrayPrint(vector<vector<int> > arr, int n) {
vector<int> res;
if (n == 0)
return res;
if (n == 1)
{
res.push_back(arr[0][0]);
return res;
}
res.push_back(arr[0][n - 1]);
res.push_back(arr[0][n - 2]);
int i = 0, j = n - 2;
int flagi = n - 2, flagj = 0, flag = 0;
while (i != n - 1 || j != 0)
{
if (i != n-1 && j != n-1)
{
i++;
j++;
res.push_back(arr[i][j]);
}
else if (j == n - 1)
{
if (flagi != 0)
{
j = --flagi;
i = 0;
res.push_back(arr[i][j]);
}
else
{
j = 0;
i = ++flagj;
res.push_back(arr[i][j]);
}
}
else
{
j = 0;
i = ++flagj;
res.push_back(arr[i][j]);
}
}
return res;
}
4.在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益。请采用实践复杂度低的方法实现。
给定价格序列prices及它的长度n,请返回最大收益。保证长度小于等于500。
[10,22,5,75,65,80],6
返回:87
解析:每次选择一个标志位置,分别计算标志位置前的最大差值max1和标志位置及之后的最大差值max2,记录最大的差值和max = max(max1+max2)。只不过在求分区间最大max1和max2的时候,用一个min记录当前遍历到的最低股票价格,当有以后某个prices[i]-min<0时,更新min为prices[i]。
int maxProfit(vector<int> prices, int n) {
int max1, max2, max=0, min;
for (int j = 1; j < n; j++)
{
min = prices[0], max1 = 0, max2 = 0;
for (int i = 0; i <= j; i++)
{
if (prices[i] - min > max1)
max1 = prices[i] - min;
else if(prices[i] - min < 0)
min = prices[i];
}
min = prices[j];
for (int i = j+1; i < n; i++)
{
if (prices[i] - min > max2)
max2 = prices[i] - min;
else if(prices[i] - min < 0)
min = prices[i];
}
max = max > max1 + max2 ? max : max1 + max2;
}
return max;
}