LDU 软件工程 算法分析与设计(一)算法入门

exm:独立思考万岁,代码如果在你们OJ过不去,说明你们的服务器比我们还垃圾,那么建议你们更换服务器。

本人代码丑,如果对于哪里不理解而且没加注释,可以留言。

问题 A: 数字统计问题

题目描述

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

输入

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

输出

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

样例输入

11

样例输出

1
4
1
1
1
1
1
1
1
1

思想:枚举当前数字和后边的数字,对于比当前为数小的那个数,后边肯定有10^后边位数,对于当前就是后边的和。

,对于后边位数那个数出现的次数等于后边的长度*10^长度减一(自行检验),还需要减去多余的0

#include<bits/stdc++.h>
using namespace std;
int vis[10];
int main()
{
    int page;
    scanf("%d",&page);
    int len=0;
    int temp=page;
    while(temp)
    {
        len++;
        temp=temp/10;
    } 
    for(int i=0;i<len;i++)
    { 
        int A=(page/(int)pow(10,len-i-1))%10;//数 
        int B=page%(int)pow(10,len-i-1);//后
        vis[A]+=B+1;
        for(int j=0;j<A;j++)
        {
            vis[j]+=pow(10,len-i-1);
            for(int k=0;k<10;k++)
                vis[k]+=(len-i-1)*(int)pow(10,len-i-2);
        }   
    } 
    for(int i=0;i<len;i++)
        vis[0]-=(int)pow(10,i);
    for(int i=0;i<10;i++)
        printf("%d\n",vis[i]);  
    return 0;
} 

问题 D: 2011的倍数

题目描述

给定一个正整数n,多少个1组成的整数可以被n整除?

输入

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

输出

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

样例输入

2011

样例输出

670

直接模拟就好了,大概是52W个1组成的数后边如果没有就是无解,2记住要特判下。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long n;
    scanf("%lld",&n);
    if(n==2)
        printf("-1\n");
    else
    {
        int ans=1;
        long long temp=1;
        while(1)
        {
            if(temp%n==0)
            {
                printf("%d\n",ans);
                break;
            }
            temp=temp%n*10+1;
            ans++;
            if(ans>=520000)
            {
                printf("-1\n");
                break;
            }
        }
    } 
    return 0;
}

问题 E: 最多约数问题

题目描述

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

输入

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

输出

一个正整数表示答案。

样例输入

1 36

样例输出

9

思想:素因子打表,最大不会1e3超过1e3不会出现2个

#include<bits/stdc++.h>
using namespace std;
int vis[1005];
int num[1005];
int cnt;
void init()
{
    vis[1]=1;
    for(int i=2;i<=1000;i++)
    {
        if(vis[i]==0)
        {
            num[cnt++]=i;   
            for(int j=i+i;j<=1000;j+=i)
                vis[j]=1;
        } 
    } 
} 
int main()
{
    init();
    int a,b;
    int Max=0;
    scanf("%d%d",&a,&b);
    for(int i=a;i<=b;i++)
    {
        int temp=i;
        int sum=1;
        for(int j=0;j<cnt && num[j]<=temp;j++)
        {
            int ans=0;
            while(temp%num[j]==0)
            {
                ans++;
                temp=temp/num[j];
            }
            sum*=(ans+1);   
        }
        Max=max(Max,sum);
    }
    printf("%d\n",Max);
    return 0;
}

dfs爆搜

#include<iostream>
using namespace std;
typedef long long LL;
  
const int a[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int T1,T;
LL n,ans;
  
void DFS(int k,LL sum,LL ni,int m);
int main()
{
    ios::sync_with_stdio(false);
    ans=1;
    cin>>T>>n;
    DFS(0,1,1,15);
    cout<<ans<<endl;
    return 0;
}
  
void DFS(int k,LL sum,LL ni,int m)
{
    if(k>16) return;
    ans=max(ans,sum);
    for(int i=1;i<=m;++i)
        if(ni<=n/a[k]){
            ni*=a[k];
            DFS(k+1,sum*(i+1),ni,i);
        }else   break;
}

问题 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

思想:鸽笼原理 模拟下就好,注意特判下都在一个区间的情况。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e7+10;
double num[maxn];
int sum[maxn];//记录数量 
double Maxx[maxn]; //储存最大值
double Minn[maxn];//储存最小值 
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lf",&num[i]);
    double Max=-1;
    double Min=1e30;
    for(int i=0;i<n;i++)
    {
        Max=max(Max,num[i]);
        Min=min(Min,num[i]);
    } 
    double Gap=(Max-Min)/((n-1)*1.0);//间隙大小 
    if(Gap==0.0)
    {
        printf("0.0\n");
        return 0;
    }
    for(int i=0;i<n;i++)
    {
        int temp=(int)((num[i]-Min)/(Gap))+1;
        //printf("%d %lf\n",temp,num[i]); 
        if(Maxx[temp]==0)
            Maxx[temp]=num[i];
        else   
            Maxx[temp]=max(Maxx[temp],num[i]);
        if(Minn[temp]==0)
            Minn[temp]=num[i];
        else
            Minn[temp]=min(Minn[temp],num[i]);
        sum[temp]++; 
    }
    double ans=Maxx[1]-Minn[1];
    double temp=Maxx[1];
    for(int i=2;i<=n;i++)
    {
        if(sum[i]>=1)
        {
            ans=max(ans,Minn[i]-temp);
            temp=Maxx[i];
        }
    }
    printf("%.1lf\n",ans);
    return 0;
}

问题 G: 字典序问题

题目描述

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

… 

ab 

ac 

 

27 

28 

输入

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

输出

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

样例输入

2
a
b

样例输出

1
2

思想:直接递归找肯定T,所以提前递归打表,直接呜呜呜的算就好了。

#include<bits/stdc++.h>
using namespace std;
char str[10];
int dp[30][30]; 
int find(int index,int len)
{   
    int sum=0;
    if(len==1)
        return 1;
    for(int j=index+1;j<=26;j++)
    {
        if(dp[j][len-1])
            sum+=dp[j][len-1];
        else
            sum+=find(j,len-1);
    }
    dp[index][len]=sum;
    return sum;
} 
void init()
{
    for(int i=1;i<=6;i++)
    {
        for(int j=1;j<=26;j++)
            dp[j][i]=find(j,i);
    }
}
int check(char str[])
{
    int sum=0;
    int len=strlen(str);
    for(int i=1;i<len;i++)//长度小于len的 
        for(int j=1;j<=26;j++) 
            sum+=dp[j][i];
   
    int k=str[0]-'a'+1;
    for(int i=1;i<k;i++)//比第一位小的所有的 
        sum+=dp[i][len]; 
               
    int count=k;//第一位开头的那个 
    for(int i=1;i<len;i++)//枚举后边的len-1位 
    {
        int temp=str[i]-'a'+1;
        int length=len-i;        //后边的长度 
        for(int j=count+1;j<temp;j++)//保证升序,每一位都比上次的大 
            sum+=dp[j][length]; 
        count=temp; 
    }
    return sum+1;
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",str);
   
        int temp=check(str);
        printf("%d\n",temp);    
    }
    return 0;
}

问题 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

思想:先考虑后边的每列跟第一列交换,因为只能交换列或者翻转某一行,所以只要把第一列定型之后,后边只能交换列,不能在动一行了,否则会影响整个局面,因此,就把第一列跟所有的列交换一次,然后交换后,考虑A矩阵和某一列交换使当前列变成题目要求的列一样,就这样一列一列转换好,如果最终可以转换好,那么就保存下交换的次数,如果不行就不管了,最后根据次数来判断是否OK了。很多情况break掉了 所以时间复杂度卡不到1e8

#include<bits/stdc++.h>
using namespace std;
const int INF = 1<<30; 
int a[110][110];
int b[110][110];
int tmp[110][110];
int m,n;
int check(int j,int k)//比较A的k列和b的j列是否相等 
{
	for(int i=0;i<n;i++)
		if(a[i][k]!=b[i][j])
			return 0;
	return 1;
}
void find()
{
    int flag;//标记是否OK 
    int ans=INF;//最少交换次数 
    int Count=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			tmp[i][j]=a[i][j];
    for(int i=0;i<m;i++)
    {
    	for(int j=0;j<n;j++)//将当前i列与第一列进行交换 
    		swap(a[j][i],a[j][0]);
        if(i!=0)
            Count++;
        for(int j=0;j<n;j++)//判断是否第一列已经相等 
        {
            if(a[j][0]!=b[j][0])
            {
            	for(int k=0;k<m;k++)
            		a[j][k]^=1;//j行m列都变换 
                Count++;
            } 
        }
        flag=1;
		//第一列肯定是OK了
        for(int j=1;j<m;j++)//考虑交换列 
        {
            flag=0;
            for(int k=j;k<m;k++)//b数组为参考 
            {
                if(check(j,k))  //比较a的第k列和b的第j列是否相等
                {
                    if(k!=j)
                    { 
                        Count++;
                        for(int kk=0;kk<n;kk++)
							swap(a[kk][k],a[kk][j]);
                    } 
                    flag=1; //找到可以交换的了  	
                    break;
                }
            }
            if(flag==0)//没办法交换 
                break;
        }
        if(flag==1 && ans>Count)
            ans=Count;
        Count=0;
        for(int j=0;j<n;j++)
			for(int k=0;k<m;k++)
				a[j][k]=tmp[j][k];
    }
    if(ans==INF)
		printf("-1\n");
	else
		printf("%d\n",ans); 
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            scanf("%d",&a[i][j]);
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            scanf("%d",&b[i][j]);
    find();
    return 0;
}

 

专题十:算法分析设计 1.常用的算法设计方法:   1.1 迭代法   1.2 穷举搜索法   1.3 递推法   1.4 递归法   1.5 贪婪法   1.6 分治法   1.7 动态规划法   1.8 回溯法 算法基础部分: 算法是对特定问题求解步骤的一种描述,算法是指令的有限序列,其中每一条指令表示一个或多个操作。 算法具有以下5个属性:   有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都在有穷时间内完成。   确定性:算法中每一条指令必须有确切的含义。不存在二义性。只有一个入口和一个出口   可行性:一个算法是可行的就是算法描述的操作是可以通过已经实现的基本运算执行有限次来实现的。   输入:一个算法有零个或多个输入,这些输入取自于某个特定对象的集合。   输出:一个算法有一个或多个输出,这些输出同输入有着某些特定关系的量。 所以对应的算法设计的要求:   正确性:算法应满足具体问题的需求;   可读性:算法应该好读,以有利于读者对程序的理解;   健壮性:算法应具有容错处理,当输入为非法数据时,算法应对其作出反应,而不是产生莫名其妙的输出结果。   效率与存储量需求:效率指的是算法执行的时间;存储量需求指算法执行过程中所需要的最大存储空间。一般这两者与问题的规模有关。 1.1 迭代法: 迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行: (1)选一个方程的近似根,赋给变量x0; (2)将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0; (3)当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤(2)的计算。 若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为: 【算法】迭代法求方程的根 { x0=初始近似根; do { x1=x0; x0=g(x1); /*按特定的方程计算新的近似根*/ } while ( fabs(x0-x1)>Epsilon); printf(“方程的近似根是%f\n”,x0); } 迭代算法也常用于求方程组的根,令 X=(x0,x1,…,xn-1) 设方程组为: xi=gi(X) (I=0,1,…,n-1) 则求方程组根的迭代算法可描述如下: 【算法】迭代法求方程组的根 { for (i=0;i<n;i++) x[i]=初始近似根; do { for (i=0;i<n;i++) y[i]=x[i]; for (i=0;i<n;i++) x[i]=gi(X); for (delta=0.0,i=0;i<n;i++) if (fabs(y[i]-x[i])>delta) delta=fabs(y[i]-x[i]); } while (delta>Epsilon); for (i=0;i<n;i++) printf(“变量x[%d]的近似根是 %f”,I,x[i]); printf(“\n”); } 具体使用迭代法求根时应注意以下两种可能发生的情况: (1)如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制; (2)方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。 1.2 穷举搜索法: 穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从中找出那些符合要求的候选解作为问题的解。 要解决的问题只有有限种可能,在没有更好算法时总可以用穷举搜索的办法解决,即逐个的检查所有可能的情况。可以想象,情况较多时这种方法极为费时。实际上并不需要机械的检查每一种情况,常常是可以提前判断出某些情况不可能取到最优解,从而可以提前舍弃这些情况。这样也是隐含的检查了所有可能的情况,既减少了搜索量,又保证了不漏掉最优解。 【问题】 将A、B、C、D、E、F这六个变量排成如图所示的三角形,这六个变量分别取[1,6]上的整数,且均不相同。求使三角形三条边上的变量之和相等的全部解。如图就是一个解。 程序引入变量a、b、c、d、e、f,并让它们分别顺序取1至6的整数,在它们互不相同的条件下,测试由它们排成的如图所示的三角形三条边上的变量之和是否相等,如相等即为一种满足要求的排列,把它们输出。当这些变量取尽所有的组合后,程序就可得到全部可能的解。细节见下面的程序。 # include <stdio.h> void main() { int a,b,c,d,e,f; for (a=1;a<=6;a++) //a,b,c,d,e依次取不同的值 for (b=1;b<=6;b++) { if (b==a) continue; for (c=1;c<=6;c++) { if (c==a)||(c==b) continue; for (d=1;d<=6;d++) { if (d==a)||(d==b)||(d==c) continue; for (e=1;e<=6;e++) { if (e==a)||(e==b)||(e==c)||(e==d) continue; f=21-(a+b+c+d+e);//最后一个用减法算 if ((a+b+c==c+d+e))&&(a+b+c==e+f+a)) { printf(“%6d,a); printf(“%4d%4d”,b,f); printf(“%2d%4d%4d”,c,d,e); scanf(“%c”); } } } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值