LDU软件工程算法课程习题(一)

1、

问题 D: 2011的倍数(二)

题目描述

给定一个正整数n,至少多少个1组成的整数可以被n整除?(例如n=3,  111/3=37,因此答案是3,指111由3个1组成)

 

输入

一个整数n(1<=n<=105) 

 

输出

一个正整数,表示数字1的个数。如果无解,请输出-1

 

样例输入

2011

 

样例输出

670

思路:水,直接模拟除法即可找到就输出位数就可以了,要注意的是不能直接整除试试,因为c语言很容易就爆掉整数,-1的情况就是如果p整除2或者5所不存在此数字。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
	
	int num = 1;
	int flag = 0;
	int number = 1;
	int p;
	scanf("%d",&p); 
	if(p % 2 == 0 || p % 5 == 0)
	{
		printf("-1\n");
		return 0;
	} 
	while(number%p != 0)
	{
		num++;
		number %= p;
		number = number*10+1;
	}
	printf("%d\n",num);
	return 0;
}

 

2、 

问题 B: 数字统计问题(二)

题目描述

给定一本书,其中包含n页,计算出书的全部页码中用到了多少个数字0…9?页码从1开始

 

输入

一个整数n,代表页码总数。(1<=n<=109)

 

输出

十行,每行一个整数,分别表示0~9每个数字出现的次数

 

样例输入

11

 

样例输出

1
4
1
1
1
1
1
1
1
1

 

提示

注意!答案范围可能超过int存储范围。

水,我们现在假设有这么一个数62356,对于百位的数字2来说:

①:4-9出现的分别次数是多少呢?没错是62*10^2,就是0-62然后加上后面两位的任意组合

②:   3出现的次数是多少呢?没错是62*10^2+356+1,不解释了,跟上面一样,不过是要考虑当前位数后面还有356+1位。

③: 0-2出现多少次呢?没错就是63*10^2,无需解释。

④: 因为我们在考虑的时候都考虑了0,但是对于一个数字来说,前导位都是0没有意义的所以我们要删去前导0的个数字。

 

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
ll ans[15] = {0};
ll num[15];
int main()
{
	int cnt = 1;
	memset(ans,0,sizeof(ans));
	ll n;scanf("%lld",&n);
	ll m = n;
	while(m)
	{
		num[cnt++] = m%10;
		m /= 10;
	}
	for(int i = 1;i < cnt;i++)
	{
		ll high =  (n/(ll)pow(10,i));
		ll low = n%(ll)pow(10,i-1);
		ans[num[i]] += high*pow(10,i-1)+low+1;
		for(int j = 0;j < num[i];j++)
			ans[j] += (high+1)*pow(10,i-1);
		for(int j = num[i]+1;j < 10;j++)
			ans[j] += high*pow(10,i-1);
	}
	for(int i = 1;i < cnt;i++)
		ans[0] -= pow(10,i-1);
	for(int i = 0;i < 10;i++)
	printf("%lld\n",ans[i]);
}

 

3、

问题 G: 字典序问题

题目描述

在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表A由26个小写字母组成。该字母表产生的升序字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次。例如,a,b,ab,bc,xyz等字符串都是升序字符串。现在对字母表中产生的所有长度不超过6的升序字符串,计算它在字典中的编码。

… 

ab 

ac 

 

27 

28 

 

 

输入

第一行一个整数T,表示测试组数(1<=T<=1e6)。
接下来T行,每行一个长度不超过6的升序字符串(仅含小写字母)。 

 

输出

输出T行,每行一个整数代表答案。

 

样例输入

2
a
b

 

样例输出

1
2

 

[提交][状态]

 

思考一下某个字母打头有k位的数字有多少个,对于1位来说就是1,对于两位比如a开头两位有25个,b开头有24..以此类推,对于三位a开头有24+23+.....1,可以看出就是第i+1到26个字母前k-1位的和

 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll ans[30][10];
char s[10];
int main()
{

	while(~scanf("%s",s))
	{
		  memset(ans,0,sizeof(ans));
		for(int i = 1;i <= 6;i++)
		{
			for(int j = 1;j <= 26;j++)
			{
				if(i == 1)
				ans[j][i] = 1;
				else
					for(int k = j+1;k <= 26;k++)
						ans[j][i] += ans[k][i-1];
			}
		}
		/* 
		for(int i = 1;i <= 26;i++)
			for(int j = 1;j <= 6;j++)
				printf("%d%c",ans[i][j],j == 6?'\n':' ');
		*/ 
		int len = strlen(s);
		ll res = 0; 
		for(int i = 1;i < len;i++)//加上前n-1位 
		{
			for(int j = 1;j <= 26;j++)
				res += ans[j][i];
		}
		for(int i = 0;i < len;i++)//枚举每一位然后加上 
		{
			if(i  == 0)
			{
				for(int j = 1;j < s[i]-'a'+1;j++)
					res += ans[j][len-i];
			}
			else
			{
				for(int j = s[i-1]-'a'+2;j < s[i]-'a'+1;j++)
					res += ans[j][len-i];
			}
		}
		res++;//最好要加上本身这个顺序 
		printf("%lld\n",res);
	}

	return 0;
}

 

4、

问题 E: 最多约数问题

题目描述

正整数 x 的约数是能整除x的正整数,其约数的个数记为div(x),例如div(10)=4。设 a 和 b 是两个正整数,找出 a 和 b 之间(包含a,b)约数个数最多的数 x 的约数个数

 

输入

两个正整数a和b,(1<=a<=b<=105)

 

输出

一个正整数表示答案。

 

样例输入

1 36

 

样例输出

9

涉及到一个质因数分解,比如36 = 2*2*3*3 素数2和3分别出现2次,所以最后可以凑成的约数个数就是(2+1)*(2+1) = 9;

 

#include<bits/stdc++.h>
using namespace std;
typedef  long long ll; 

ll num(ll x)
{
	ll a[105] = {0},b[105]= {0};
	ll res = 1,tot = 0;
	ll now = x;
	ll temp = (ll)sqrt((double)x)+1;
	for(int i = 2;i < temp && i < now;i++)
	{
		if(now % i == 0)
		{
			a[++tot] = i;
			while(now%i == 0)
			{
				b[tot]++;
				now /= i;
			}
		}
	}
	if(now != 1)
	{
		a[++tot] = now;
		b[tot]++;
	}
	for(int i = 1;i <= tot;i++)
	{
		res *= (b[i]+1);
	}
	return res;
}
int main()
{
	ll a,b;
	while(~scanf("%lld%lld",&a,&b))
	{
		ll ans = -100;
		for(int i = a;i <= b;i++)
		{ 
			ans = max(ans,num(i));
		}
		printf("%lld\n",ans);
	}
	return 0;
}

5、

问题 F: 最大间隙问题

题目描述

给定 n 个实数,求这n个实数在数轴上相邻2个数之间的最大差值,设计解最大间隙问题的线性时间算法(时间复杂度为O(n))。 

 

输入

第一行一个正整数n(2<=n<=2×107) 
第二行n个实数,数据保证这些实数只有一位小数。 

 

输出

一个实数,保留1位小数。

 

样例输入

5
2.3  3.1  7.5  1.5  6.3

 

样例输出

3.2

(时间复杂度为O(n))。 

要在线性的时间内找出相邻的最大间隙,排序肯定是不行的,因为最好的算法也是n*logn级别,如果是相邻的间隙中找出最大值,那么完全可以把这n个数放在n-1个区间,根据鸽笼原理,除去最头和最尾那么肯定有一个区间是没有数的,在这个区间外的有数的两个区间里的最小值和最大值极有可能是最大间隙,因为扫一遍区间然后就可以找到最大值。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e7+10;
double a[maxn],up[maxn],down[maxn];
int vis[maxn];
int main()
{
	int n;scanf("%d",&n);
	for(int i = 0; i < n;i++)
	{
		up[i] = -1000000000;
		down[i] = 1000000000;
	}
	double Max,Min; 
	for(int i = 0; i < n;i++)
	{
		scanf("%lf",a+i);
		if(i == 0)
		{
			Max = a[i];
			Min = a[i];
			continue;
		}
		Max = max(a[i],Max);
		Min = min(a[i],Min);
	}
	if(Max == Min)
	{
		printf("0.0\n");//最大和最小值相等那么肯定间隙为0 
		return 0;
	}
	double len = (Max-Min)/(n-1);
	for(int i = 0;i < n;i++)//记录每个区间最值 
	{
		int block = (int)((a[i]-Min)/len);
	
		if(vis[block] == 0)
		{
			up[block] = a[i];
			down[block] = a[i];
			vis[block] = 1;
		}
		else
		{
			up[block] = max(up[block],a[i]);
			down[block] = min(down[block],a[i]);	
		}
	
	}
	double l,r;
	double ans = 0;
	for(int i = 0; i < n-1;i++)//扫一遍区间 
	{
		if(vis[i])
		{
			l = up[i];
			int j = i+1;
			while(j <= n-1)
			{
				if(vis[j])
				{
					r = down[j];
					ans = max(ans,(r-l));
					i = j-1;
					break;
				}
				j++;
			}
		}
	}
	printf("%.1lf\n",ans);
	return 0;
 } 

 

6、

问题 H: 金币阵列问题

题目描述

n×m(m<=100,n<= 100)个金币在桌面上排成一个n列的金币阵列。每一枚金币或正面朝上或背面朝上。用数字表示金币状态,0表示金币正面朝上,1 表示背面朝上。金币阵列游戏的规则是:

(1)每次可将任一行金币翻过来放在原来的位置上; 

(2)每次可任选2 列,交换这2 列金币的位置。 
给定金币阵列的初始状态和目标状态,编程计算按金币游戏规则,将金币阵列从初始状态变换到目标状态所需的最少变换次数。 

 

输入

每组数据的第1行有2个正整数n和m。以下的n行是金币阵列的初始状态,每行有m 个数字表示该行金币的状态,0 表示金币正面朝上,1 表示背面朝上。接着的n行是金币阵列的目标状态。

 

输出

输出最少变换次数(规则1和2使用的次数之和),无解时输出-1。

 

样例输入

4 3
1 0 1
0 0 0
1 1 0
1 0 1

1 0 1
1 1 1
0 1 1
1 0 1

 

样例输出

2

 

就是因为如果交换的话肯定是有一列跟第一列交换,所以只需要枚举那一列跟第一列交换,然后找最小的步数就可以了

 

#include<bits/stdc++.h>
using namespace std;
int n,m;
int goal[105][105],change[105][105],ever[105][105];
int step,ans = 10000005;
void show()
{
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
    printf("%d%c",change[i][j],j == m?'\n':' ');
}
void copy()
{
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            change[i][j] = ever[i][j];              
}
void swap(int a,int b)
{
    for(int i = 1;i <= n;i++)
    {
        int temp;
        temp = change[i][a];
        change[i][a] = change[i][b];
        change[i][b] = temp;
    }
}
void changerow(int a)
{
    for(int i =1;i <= m;i++)
    {
        if(change[a][i] == 1)
        change[a][i] = 0;
        else
        change[a][i] = 1;
    }
}
void check()
{
    for(int i = 1;i <= n;i++)
    {
        if(goal[i][1] != change[i][1])
        {
            changerow(i);
            step++;
        }
         
    }
}
bool ok(int a,int b)
{
    for(int i = 1;i <= n;i++)
    {
        if(goal[i][a] != change[i][b])
        return false;
    }
    return true;
}
bool judge()
{
    for(int i = 2;i <= m;i++)
    {
        for(int j = i;j <= m;j++)
        {
            if(ok(i,j))
            {
                if(i != j)
                {
                    swap(i,j);
                    step++;
                }
                 
                break;
            }
            if( j == m)
            return false;
        }
    }
    return true;
}
void solved()
{
    for(int i = 1;i <= m;i++)
    {
        step = 0;
        copy();
        swap(1,i);
        if(i != 1)
        step++;
        check();
        if(judge())
        ans = min(ans,step);
    }
    if(ans == 10000005)
    printf("-1\n");
    else
    printf("%d\n",ans);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
        scanf("%d",&goal[i][j]);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
        scanf("%d",&ever[i][j]);
    solved();
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值