期末算法edu

第二章

设计算法求解整数的划分问题

/*将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。正整数n 的不同的划分个数称为正整数n 的划分数。

对给定的整数,输出对应的划分数
测试输入:5
预期输出:
7
*/
#include<stdio.h>

const int N=1000;
int n;
int cnt=0;

int dfs(int n,int m){
	if(n==1||m==1){
		return 1;
	}
	if(n>m){
		return dfs(n-m,m)+dfs(n,m-1);//不包含包含
	}
	if(n>m){
		return dfs(n,n);
	}
	return dfs(n,n-1)+1;
}
int main(){
	scanf("%d",&n);
	print("%d",dfs(n,n));
	return 0
}

有重复元素的排列问题

/*设集合R={r1,r2,…,rn}是要进行排列的n个元素,其中r1,r2,…,rn可能相同。
试着设计一个算法,列出R的所有不同排列。
即,给定n以及待排的n个可能重复的元素。计算输出n个元素的所有不同排列
测试输入:
4
aacc

预期输出:
aacc
acac
acca
caac
caca
ccaa
6
*/
#include<stdio.h>

const int N=1000;
int n;
int cnt=0;
char arr[N],state[N];
int used[N]={0};
void dfs(char ch[],int n,char b[],int p){
	if(p==n){
		printf("%s\n",b);
		cnt++;
		return;
	}
	for(int i=0;i<n;i++){
		if(i>0&&ch[i]==ch[i-1]&&!used[i-1]) continue;
		if(!used[i]){
			used[i]=1;
			b[p]=ch[i];
			dfs(ch,n,b,p+1);
			used[i]=0; 
		}
	}
}

int main(){
	scanf("%d",&n);
	scanf("%s",arr);
	char b[n+1]="";
	dfs(arr,n,b,0);
	printf("%d",cnt);
}

棋盘覆盖问题

/*在一个2k×2k个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
易知,覆盖任意一个2k×2k的特殊棋盘,用到的骨牌数恰好为(4K-1)/3
测试输入:
3 3 2
预期输出:
3   3   4   4   8   8   9   9
3   2   2   4   8   7   7   9
5   2   6   6   10  10  7   11
5   5   0   6   1   10  11  11
13  13  14  1   1   18  19  19
13  12  14  14  18  18  17  19
15  12  12  16  20  17  17  21
15  15  16  16  20  20  21  21
*/
#include<stdio.h>
#include<math.h>

int k;
int x,y;
const int max=1<<10;
int board[max][max];
int number;//l型牌编号

void chessboard(int row,int column,int x,int y,int siz){
	//递归出口
	if(siz==1){
		return;
	} 
	
	int s=siz/2;//对半划分成2^(siz - 1) * 2^(siz - 1)的棋盘
	int t=++number;//l型编号自增
	int centerRow=row+s;//中间点,以此判别(x,y)在哪个子棋盘中 
	int centerColumn=column+s;
	//黑色方格在左上子棋盘
	if(x<centerRow&&y<centerColumn){
		chessboard(row,column,x,y,s);
	}else{
		// 不在则填充左上子棋盘的右下角
		board[centerRow-1][centerColumn-1]=t;
		// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
		chessboard(row,column,centerRow-1,centerColumn-1);
	}
	 // 黑色方格在右上子棋盘
    if(x < centerRow && y >= centerColumn) {
        chessboard(row, centerColumn, x, y, s);
    } else {
        // 不在则填充右上子棋盘的左下角
        board[centerRow - 1][centerColumn] = t;
        // 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
        chessboard(row, centerColumn, centerRow - 1, centerColumn, s);
    }
    // 黑色方格在左下子棋盘
    if(x >= centerRow && y < centerColumn) {
        chessboard(centerRow, column, x, y, s);
    } else {
        // 不在则填充左下子棋盘的右上角
        board[centerRow][centerColumn - 1] = t;
        // 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
        chessboard(centerRow, column, centerRow, centerColumn - 1, s);
    }

    // 黑色方格在右下子棋盘
    if(x >= centerRow && y >= centerColumn) {
        chessboard(centerRow, centerColumn, x, y, s);
    } else {
        // 不在则填充右下子棋盘的左上角
        board[centerRow][centerColumn] = t;
        // 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
        chessboard(centerRow, centerColumn, centerRow, centerColumn, s);
    }

} 

int main(){
	scanf("%d",&k);
	scanf("%d%d",&x,&y);
	int siz=pow(2,k);
	
	board[x][y]=0;
	number=0;
	chessboard(0,0,x,y,siz);
	for(int i=0;i<siz;i++){
		for(int j=0;j<siz;j++){
            if(j==siz-1){
                printf("%d",board[i][j]);
            }
            else{
		    	printf("%-4d",board[i][j]);
            }
		}
		printf("\n");
	}
	
	
	return 0;
}

全排列

//实现全排列,递归实现
//do{}while(next_permutation(a,a+n))
#include <stdio.h>
#include <stdlib.h>
int n=0;

void swap(int *a, int *b)
{
     int m;
     m=*a;
     *a=*b;
     *b=m;
}
void perm(int list[], int k, int m)
{
     int i;
     if(k==m)
     {
             for(i=0;i<=m;i++)
                              printf("%d ",list[i]);
             printf("\n");
             n++;
     }
     else
     {
         for(i=k;i<=m;i++)
         {
                          swap(&list[k],&list[i]);
                          perm(list, k+1, m);
                          swap(&list[k], &list[i]);
         }
     }
}
int main(void)
{
    int list[]={1,2,3,4,5,6,7};
    perm(list,0,6);
    printf("total:%d\n",n);
    system("pause");
    return 0;
}

//求字典顺序的下一个全排列
#include <stdio.h>
#include <stdlib.h>
void swap(int *a, int *b)
{
     int m;
     m=*a;
     *a=*b;
     *b=m;
}
void perm(int list[], int len)
{
     int i=0;
     int k=0;
     int n=len;
     int j=1;
     for(;j<=len;j++)
     {
                     if(list[j-1]<list[j])
                                          i=j;
     }
     for(j=1;j<=len;j++)
     {
                        if(list[i-1]<list[j])
                                             k=j;
     }
     swap(&list[i-1],&list[k]);
     for(j=0;j<=i;j++)
     {
                      printf("%d ",list[j]);
     }
     for(j=len;j>i;j--)
     {
                       printf("%d ",list[j]);
     }
}
int main(void)
{
    int list[]={7,6,4,1,3,5,2};
    perm(list,6);
    system("pause");
    return 0;
}

最大子段和

#include<cstdio>
#include<algorithm>
using namespace std;
int a[110];
int maxsum(int l,int r)
{
    if(l==r)
    {
        return a[l];
    }
    int mid = (l+r)/2;
    int lsum = maxsum(l,mid);  //左区间
    int rsum = maxsum(mid+1,r);//右区间
    int sum1=0,sum2=0;
    int lefts=0,rights=0;      //跨界求和
    for(int i=mid; i>=l; i--)
    {
        lefts+=a[i];
        if(lefts>sum1)
        {
            sum1=lefts;
        }
    }
    for(int i=mid+1; i<=r; i++)
    {
        rights+=a[i];
        if(rights>sum2)
        {
            sum2=rights;
        }
    }
    int msum=sum1+sum2;
    return max(msum,max(lsum,rsum)); //三者中的最大值
}

int main()
{
    int n;
    int ans;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1; i<=n; i++)
        {
           scanf("%d",&a[i]);
        }
        ans = maxsum(0,n);
        if(ans<0)
        {
            ans=0;
        }
       printf("%d\n",ans);
    }
}

第三章

编程实现矩阵连乘问题的求解

/*在计算矩阵连乘积时,加括号的方式对计算量有影响。

例如有三个矩阵A1,A2,A3连乘,它们的维数分别为
10*100,100*5,5*50。用第一种加括号方式(A1A2)A3计算,则所需数乘次数为10*100*5+10*5*50=7500。用第二种加括号方式A1(A2A3)计算,需要100*5*50+10*100*50=75000次数乘。

输入连乘矩阵的个数,每个矩阵的维数。要求输出最少数乘次数。*/
输入
6
30 35
35 15
15 5
5 10
10 20
20 25

输出
15125

#include <stdio.h>

const int N=100;
int p[N];
int m[N][N];
int s[N][N];

void MatrixChain(int n)
{
	for (int i = 1; i <= n; i++) m[i][i] = 0;
	for (int r = 2; r <= n; r++)
		for (int i = 1; i <= n - r+1; i++) 
		{
			int j=i+r-1;
			m[i][j] = m[i+1][j]+ p[i-1]*p[i]*p[j];
			s[i][j] = i;
			for (int k = i+1; k < j; k++) 
			{
				int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
				if (t < m[i][j]) { m[i][j] = t; s[i][j] = k;}
			}
		}
}

void TraceBack(int i, int j) 
{ 
	if(i==j) return;
	else 
	{
		TraceBack(i,s[i][j]); 
		TraceBack(s[i][j]+1,j); 
	}
} 

int main() 
{
	int n;
	scanf("%d", &n);
	int i, temp;
	for (i=0; i<n; i++)
		scanf("%d%d", &p[i], &temp);
	p[n] = temp;
	MatrixChain(n);
	TraceBack(1, n);
	printf("%d\n", m[1][n]);
	return 0;
}

编程实现最大子段和

/*对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。*/

#include<stdio.h>

int Maxsum(int *arr,int left,int right){
	int sum=0;
	if(left==right){
		return arr[right]>0 ? arr[right] : 0; 
	} 
	else{
		int center=(left+right)/2;
		int leftsum=Maxsum(arr,left,center);
		int rightsum=Maxsum(arr,center+1,right);
		
		int lm=0;//左最大 
		int leftm=0;//左和
		int rm=0;//右最大 
		int rightm=0;//右和
		
		for(int i=center;i>=left;i--){
			leftm+=arr[i];
			if(leftm>lm){
				lm=leftm;
			}
		} 
		for(int i=center+1;i<=right;i++){
			rightm+=arr[i];
			if(rightm>rm){
				rm=rightm;
			}
		} 
		sum=lm+rm;
		if(sum<leftsum)
			sum=leftsum;
		if(sum<rightsum)
			sum=rightsum;
	}
	return sum;
}

int main(){
	int n;
	scanf("%d",&n);
	int arr[n+1];
	for(int i=1;i<=n;i++){
		scanf("%d",&arr[i]);
	}
	printf("%d",Maxsum(arr,1,n));
	return 0;
}

0-1背包

/*给定n个物品和一背包,物品i的重量是wi,其价值为vi,背包的容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?*/

#include<stdio.h>
#include<string.h>
int dp[1001][1001]={0};
int volume[1001];//体积
int value[1001];//价值

int max(int a, int b)
{
	if(a>b) return a;
	return b;
}

int main()
{
    int n,v;
    memset(dp,0,sizeof(dp));
    scanf("%d%d",&n,&v);
    for(int i=1;i<=n;i++)
    	scanf("%d%d",&volume[i],&value[i]);//从1开始
    int maxNum =0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=v;j++)
        {
            dp[i][j] = dp[i-1][j];//初始填充
            if(j>=volume[i])
            {
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-volume[i]]+value[i]);
                if(dp[i][j]>maxNum)maxNum = dp[i][j];
            }

        }
	printf("%d\n",maxNum);
    return 0;
}

最长单调子序列

/*给定一个序列,求这个序列的最长上升子序列的长度,并输出这个最长上升子序列,题目保证,最长上升子序列只有一个。*/

 #include<stdio.h>
int a[100],s[100],dis,cot,n,maxz; //s数组用来存放最长上升子序列的内容;
int dp[100]; //dp[i]表示以i位置结尾的最长上升子序列的长度 ;

int max(int a, int b)
{
	if(a>b) return a;
	return b;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		dp[i]=1;
		for(int j=1;j<=i-1;j++)
		{
			if(a[j]<a[i])
			{
				dp[i]=max(dp[i],dp[j]+1);
				if(dp[i]>=maxz)
					dis=i; //用来记录此时的最长上升子序列的最后一个数在原序列中的位置;
					maxz=max(maxz,dp[i]); //maxz记录最长上升子序列的长度;
			}
		}
	}
	printf("%d\n",maxz);//输出长度
	int count=1,cot=maxz;
	s[1]=a[dis];
	for(int i=dis-1;i>=1;i--)
	{
		if(dp[i]==cot-1)
		{
			s[++count]=a[i];
			cot=dp[i];
		}
	}
	for(int i=count;i>=1;i--) printf("%d ",s[i]);// 输出序列
	return 0;

}

最长公共子序列(LCS)

/*给定两个序列X=ABCBDAB, Y=BDCABA,求X和Y的最长公共子序列*/

 #include<stdio.h>
#include<string.h>
int c[200][200];   //用c[i][j]记录X[i]与Y[j] 的LCS 的长度
int b[200][200];   //b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向
char f[200];
 
 
/*取c[i-1][j]和c[i][j-1]的最大值,并记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向*/
int Max(int m,int n,int i,int j)
{
	if(m > n)
	{
		b[i][j] = -1;
		return m;
	}
	else
	{
		b[i][j] = 1;
		return n;
	}
}


int LCS(char x[],char y[])
{
	int i,j;
	int x_len,y_len;
	x_len = strlen(x);
	y_len = strlen(y);
	for(i = 1;i <= x_len;i++)
	{
		for(j = 1;j <= y_len;j++)
		{
			if(x[i-1] == y[j-1])
			{
				c[i][j] = c[i-1][j-1] +1;
				b[i][j] = 0;
			}
			else
			{
				c[i][j] = Max(c[i-1][j],c[i][j-1],i,j);
			}
		}
	}
	return c[x_len][y_len];
}

int main()
{
	char X[200],Y[200];
	int i,j,s;
	scanf("%s",X);
	scanf("%s",Y);
	s = LCS(X,Y);
	printf("%d\n",s);
	return 0;
}

聪明的寻宝人

/*本关任务:计算寻宝人所能带走的宝物的最大价值。

一个寻宝人在沙漠中发现一处神秘的宝藏,宝藏中共有n个宝物(n不超过20),每个宝物的重量不同,价值也不同,宝物i的重量是wi,其价值为vi。

寻宝人所能拿走的宝物的总重量为m(m不超过50)。请帮助寻宝人写一个程序,计算寻宝人能够获得的最大总价值。*/

#include <iostream>
using namespace std;

void MaxValue(int values[],int weights[],int n,int m)
{
	/**********   Begin   **********/
	//补充代码完成任务
    int dp[n][m+1];
    for(int i=0;i<n;i++){
        dp[i][0]=0;
        for(int j=1;j<m+1;j++){
                if(i==0){
                    if(j>weights[i]){
                        dp[i][j]=values[i];
                    }
                    else{
                         dp[i][j]=0;
                    }
                 
                }
                else{
                    if(j<weights[i]){
                        dp[i][j]=dp[i-1][j];
                    }
                    else{
                       dp[i][j]=dp[i-1][j-weights[i]]+values[i]>dp[i-1][j]?dp[i-1][j-weights[i]]+values[i]:dp[i-1][j];
                    }           

                }

           }
    }
 printf("%d",dp[n-1][m]);
	
	/**********   End   **********/
}

基因检测

/*本关任务:找出两个串的最长公共子串的长度。

用一个字符串表示一段基因,例如:CTATGGGTTT。两段基因的相似度定义为它们所包含的最大公共子串的长度。

例如:CCTTGG和TGGGC的最大公共子串为TGG,它的长度为3,则我们称CCTTGG和TGGGC的相似度为3。
现给定两段基因,要求计算它们的相似度。*/

#include <iostream>
#include <algorithm>
#include<cstring>
using namespace std;

void Similar(char *str1,char *str2)
{
	/**********   Begin   **********/
	
	//补充代码完成任务
	int c[200][200];
	int i,j;
	int x_len,y_len;
	x_len = strlen(str1);
	y_len = strlen(str2);
	int flag=1;
	for(i = 1;i <= x_len;i++)
	{
		for(j = 1;j <= y_len;j++)
		{
			if(str1[i-1] == str2[j-1])
			{
				if(flag==1){
					c[i][j] = c[i-1][j-1] +1;
				}
				else{
					c[i-1][j-1]=0;
					c[i][j] = c[i-1][j-1] +1;
					flag=1;
				}
				
			}
			else
			{
				flag=0;
				c[i][j] = c[i-1][j]>c[i][j-1]?c[i-1][j]:c[i][j-1];
			}
		}
	}
	printf("%d",c[x_len][y_len]);
	/**********   End   **********/
}

找相似串

/*本关任务:找出最接近的相似串。

一般情况下,度量两个串S1和S2的相似性,可以通过从一个串变换成另一个串所需要的最少操作次数来衡量,需要的操作次数越少,则越相似。

假设从一个串变化成另一个串所允许的操作只有两种:插入一个字符或者删除一个字符。无论是插入还是删除一个符号,均算作一次操作。

现给你一个串S,和一个串的集合T,让你找出集合T中与S最相似的串*/

#include <iostream>
#include <cstring>
using namespace std;

int max_l(char *str1,char *str2)
{
	/**********   Begin   **********/
	
	//补充代码完成任务
	int c[200][200];
	int i,j;
	int x_len,y_len;
	x_len = strlen(str1);
	y_len = strlen(str2);
	int flag=1;
	for(i = 1;i <= x_len;i++)
	{
		for(j = 1;j <= y_len;j++)
		{
			if(str1[i-1] == str2[j-1])
			{
				c[i][j] = c[i-1][j-1] +1;
			}
			else
			{
				c[i][j] = c[i-1][j]>c[i][j-1]?c[i-1][j]:c[i][j-1];
			}
		}
	}
	return c[x_len][y_len];
	/**********   End   **********/
}

void Similar()
{
	/**********   Begin   **********/
	
	//补充代码完成功能
	char a[50];
	scanf("%s",a);
	int n;
	scanf("%d",&n);
	char b[n][50];
	int c[n],len1=strlen(a),min;
	for(int i=0;i<n;i++){
	    scanf("%s",b[i]);
	}
	for(int i=0;i<n;i++){
	    c[i]=max_l(a,b[i]);
	    int len2=strlen(b[i]);
	    int into=len1-c[i];
	    int del=len2-c[i];
	    c[i]=del+into;
	    if(i==0){min=c[i];}
	    min=min<c[i]?min:c[i];
	
	}
	for(int i=0;i<n;i++){
	    if(c[i]==min){
	       printf("%s\n",b[i]);
	    }
	}

	/**********   End   **********/
}

第四章

活动安排

/*学校的小礼堂每天都会有许多活动,有时间这些活动的计划时间会发生冲突,需要选择出一些活动进行举办。
小刘的工作就是安排学校小礼堂的活动,每个时间最多安排一个活动。现在小刘有一些活动计划的时间表,他想
尽可能的安排更多的活动,请问他该如何安排*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>

const int N=10010;
int n;

struct Time
{
	int B;
	int E;
	}s[N];

int cmp(const void * a,const void * b){
	if((*(struct Time *)a).B==(*(struct Time *)b).B){
		if((*(struct Time *)a).E<=(*(struct Time *)b).E){
			return -1;
		}
		return 1;
	}
	else{
		if((*(struct Time *)a).E<(*(struct Time *)b).E){
			return -1;
		}
		return 1;
	}
} 


int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d%d",&s[i].B,&s[i].E);
	}
	qsort(s,n,sizeof(struct Time),cmp);//升序 
	int cnt=0;
	int t=0;
	for(int i=0;i<n;i++){
		if(s[i].B>=t){
			cnt++;
			t=s[i].E;
		}		
	}
	printf("%d\n",cnt);
	return 0;
}

最优装载问题

/*有一天,海盗截获了一艘装满各种各样古董的货船,每一件古董都价值连城,一但打碎就失去了价值,虽然海
盗船足够大,但载重量为C,每件古董的重量为Wi,海盗们如何把尽量多的宝贝装上海盗船呢?*/
 #include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>

const int N=100010;
int n,c;
int w[N]; 

int cmp(const void *a ,const void *b)
{
	return *(int *)a-*(int *)b;
}

int main(){
	scanf("%d%d",&n,&c);
	for(int i=0;i<n;i++){
		scanf("%d",&w[i]); 
	}
	int cnt=0;
	qsort(w,n,sizeof(int),cmp);
	for(int i=0;i<n;i++){
		if(w[i]<=c){
			c-=w[i];
			cnt++;
		}
	}
	printf("%d\n",cnt);
	return 0;
}

背包问题

/*现在有很多物品(它们是可以分割的),每个物品有多个,我们知道它们每个物品的单位重量的价值v和重量w
(1<=v,w<=10);如果给你一个背包它能容纳的重量为m(10<=m<=20),你所要做的就是把物品装到背包里,
使背包里的物品的价值总和最大。*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>

const int N=20;
int s,m;
struct jiazhi{
	int w;
	int v;
}jia[N];

int cmp(const void * a,const void * b){
	if((*(struct jiazhi *)a).v>(*(struct jiazhi *)b).v){
		return -1;
	}
	return 1;
	
} 

int main(){
	scanf("%d%d",&s,&m);
	for(int i=0;i<s;i++){
		scanf("%d%d",&jia[i].v,&jia[i].w);
	}
	int sum=0;
	qsort(jia,s,sizeof(struct jiazhi),cmp);
	for(int i=0;i<s;i++){
		if(jia[i].w>=m){
			sum+=m*jia[i].v;
			break;
		}
		else{
			m-=jia[i].w;
			sum+=jia[i].w*jia[i].v;
		}
	}
	printf("%d\n",sum); 
	return 0;
}

会场安排

/*假设要在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的贪心算法进行安排。
(这个问题实际上是著名的图着色问题。若将每一个活动作为图的一个顶点,不相容活动间用边相连。使相邻顶点
着有不同颜色的最小着色数,相应于要找的最小会场数*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>

const int N=10010;
int n;
int ch[N]; 

struct Time
{
	int B;
	int E;
	}s[N];

int cmp(const void * a,const void * b){
	if((*(struct Time *)a).B==(*(struct Time *)b).B){
		if((*(struct Time *)a).E<=(*(struct Time *)b).E){
			return -1;//排到前面 
		}
		return 1;
	}
	else{
		if((*(struct Time *)a).E<(*(struct Time *)b).E){
			return -1;
		}
		return 1;
	}
} 


int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d%d",&s[i].B,&s[i].E);
	}
	qsort(s,n,sizeof(struct Time),cmp);//升序 
	int cnt=0;
	for(int i=0;i<n;i++){
		for(int j=0;j<=cnt;j++){
			if(s[i].B>=ch[cnt]){
				ch[cnt]=s[i].E;
				break;
			}
			else{
				cnt++;
				ch[cnt]=s[i].E;
				break;
			}
		}
	}
	printf("%d\n",cnt);
	return 0;
}

删数问题

/*有一个长度为n(n <= 240)的正整数,从中取出s(s < n)个数,使剩余的数保持原来的次序不变,求这
个正整数经过删数之后最小是多少*/
 #include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>

const int N=250;
char s[N];
int n;

int main(){
	scanf("%s",&s);
	scanf("%d",&n);
	while(n){
		for(int i=0;i<strlen(s);i++){
			if(s[i]>s[i+1]){
				for(int j=i;j<strlen(s);j++){
					s[j]=s[j+1];
				}
				n--;
				break;
			} 
		}
	}
	for(int i=0;i<strlen(s)-n;i++){
		printf("%c",s[i]);
	}
	return 0;
} 

删除数字问题

/*本关任务:掌握贪心算法的算法思想,并能利用贪心算法的算法思想解决删除数字问题。

给定n个纯数字组成的数字串,删除其中k(k<n)个数字后,剩下的数字按原来的秩序组成一个新的正整数,确定删除方案,使得剩下的数字组成的新的正整数最大*/


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int main(int argc, const char * argv[]) {
    
    char s[1001];
    int a[1001];
    int k;
    int n;
    
    scanf("%s",&s);
	scanf("%d",&k);
	int len=strlen(s);
	for(int i=0;i<strlen(s);i++){
		a[i]=s[i]-'0';
	}
	int m=len;
	while(k){
		for(int i=0;i<len;i++){
			int j=i+1;
			while(a[j]==-1){
				j++;
			}
			if(a[i]<a[j]&&a[i]!=-1){
				a[i]=-1;
				
				k--;
				break;
			} 
			if(i==m-1){
				a[i]=-1;
				k--;
				m--;
				break;
			}
		}
	}
	for(int i=0;i<len;i++){
		if(a[i]==-1) continue;
		else	printf("%d",a[i]);
		
		
	}
    
    return 0;
}

第五章

排列

/*设计算法从前m个大写字母(m≤26)种取出n个字母的所有排列(组合),并编程实现*/
#include<stdio.h>
int m,n;
int a[30];

bool check(int i){
	for(int j=0;j<i;j++){
		if(a[i]==a[j]){
			return false;
		}
	}
	return true;
}

void backtrack(int i,int m,int n){
	if(i>=n){
		for(int i=0;i<n;i++){
			if(i==n-1){
				printf("%c",'A'+a[i]);
			}
			else printf("%c ",'A'+a[i]);
		}
		printf("\n");
	}
	else{
		for(int j=0;j<m;j++){
			a[i]=j;
			if(check(i)){
				backtrack(i+1,m,n);
			}
		}
	}
}

int main(){
	scanf("%d%d",&m,&n);
	
	int a[m];
	backtrack(0,m,n);
	
	return 0;
}

子集合

/*设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1
中的元素之和为c。试设计一个解子集和问题的回溯法*/
#include<stdio.h>

int s[100];
int s1[100];//解集(0/1)
int n;
int c;
int cw;
int r;
int best;
bool flag;

void BackTrack(int i)
{
    if (i > n) {
        if (cw == c) {
            int cnt=0;
            for (int i = 1; i <= n; i++) 
                if (s1[i] == 1&&cnt==0) {cnt++;printf("%d",s[i]);}
                else if(s1[i] == 1&&cnt!=0) printf(" %d",s[i]);
            printf("\n");
            flag = false;
        }
        best = cw;
        return;
    }
    if (cw + s[i] <= c) {
        s1[i] = 1;
        cw += s[i];
        BackTrack(i + 1);
        cw -= s[i];
    }
    r -= s[i];
    if (cw + r > best) {
        s1[i] = 0;
        BackTrack(i + 1);
    }
    r += s[i];
 }
 int main()
 {
 	scanf("%d%d",&n,&c);
 	r = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&s[i]);
        r+= s[i];//初始值为全部元素之和
    }
    
    cw = 0;
    best = 0;
    flag = true;
    BackTrack(1);
    if (flag == true)  printf("No Solution!\n");
 }

TSP问题

#include<stdio.h>

const int N=100; 
int n;
int b[8]={1,2,3,4,5,6,7};
int a[N][N],x[8];//a存距离代价,x存当前最优路径
int min=1000;//记录距离最小值
void swap(int &a,int &b)
{
	int t=a;
	a=b;
	b=t;
}
void dfs(int i,int dis)//i用以记录节点,dis用以记录到当前节点距离和
{
	if(dis>=min)
		return;
	else if(i==n)//排列保证节点不重复,因此节点到n个可比较记录结果
	{
		if(dis+a[b[i-1]][1]<min)
		{
			min=dis+a[b[i-1]][1];
			for(int j=1;j<n;j++)
				x[j]=b[j];
		}
		return;
	}
	else
	{
		for(int j=i;j<n;j++)
		{
			swap(b[i],b[j]);//选中当前节点b[i]
			dfs(i+1,dis+a[b[i-1]][b[i]]);
			swap(b[i],b[j]);
		}
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&a[i][j]);
		}
	}
	
	dfs(1,0);
	printf("%d\n",min);
	printf("1");//固定从1开始回到1
	for(int i=1;i<n;i++)
		printf(" %d",x[i]);
	printf(" 1");
	return 0;
}

n皇后问题

/*在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允
许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。*/

 #include<stdio.h>
#include<math.h>


int check(int a[],int n){//传进来行
    for(int i=0;i<n;i++){
        if(a[i]==a[n]||abs(n-i)==abs(a[n]-a[i])){//判断当前放置的位置是否与之前的放置位置是否在同一列或同斜列
            return 0;
        }
    }
    return 1;
}
void queen(int a[],int n,int &res,int len){
    if(n==len){
        res++;
    }
	else{
        for(int j=0;j<len;j++){
            a[n]=j;//记录下当前行的当前列
            if(check(a,n)){
                queen(a,n+1,res,len);
            }
        }
    }

}
int main(){
	while(1){
    	int n;
    	scanf("%d",&n);
    	if(n==0) break;
    	int a[n];
    	for(int i=0;i<n;i++){
        	a[i]=0;//i行j列 
    	}
    	int res=0;
    	queen(a,0,res,n);
    	printf("%d\n",res);
    }
    return 0;
}

0-1背包

 #include<stdio.h>
#include<stdlib.h>

void value(int w[],int v[],int b[],int n,int c,int &maxv,int len){
	if(n==len){
		int weight=0,value=0;
		for(int i=0;i<len;i++){
			if(b[i]==1){
				weight+=w[i];
				value+=v[i];	
			}
		}
		if(weight<=c){
			maxv=maxv>value?maxv:value;
		}
		return;
	}
	else{
		b[n]=1;
		value(w,v,b,n+1,c,maxv,len);
		b[n]=0;
		value(w,v,b,n+1,c,maxv,len);
	}
}

int main(){
	int n,c;
	scanf("%d%d",&n,&c);
	int w[n],v[n],b[n]={0};
	for(int i=0;i<n;i++){
		scanf("%d",&w[i]);
	}
	for(int i=0;i<n;i++){
		scanf("%d",&v[i]);
	}
	int maxv=0;
	value(w,v,b,0,c,maxv,n);
	printf("%d\n",maxv);
	return 0;
}

图的m着色问题

/*给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色
法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出
所有不同的着色法*/
#include<iostream>

using namespace std;

int n;//n个顶点 
int r;//r条边 
int m;//m种颜色 
int side[20][20];//记录边,且相连的边值为1
int color[20];//记录顶点的颜色
int plan=0;//着色方案的个数
 
void dfs(int k);
bool canpaint(int k,int c);//顶点k是否能涂颜色c
 
int main()
{
	int i,u,v;
	cin>>n>>r>>m;
	for(i=0;i<r;i++)
	{
		cin>>u>>v;
		side[u][v]=side[v][u]=1;//进行比较的时候不能因为顶点的顺序不同值有影响 
	}
	dfs(0);//从第零个顶点开始搜索 
	cout<<plan<<endl;
	return 0;
}

void dfs(int k)
{
	if(k==n)//一条线搜索到叶子节点 
	{
		plan++;
	}
	else
	{
		for(int c=1;c<=m;c++)//试遍所有的颜色 
		{
			if(canpaint(k,c))
			{
				color[k]=c;
				dfs(k+1);
			}
		}
	}
}

bool canpaint(int k,int c)
{
	for(int i=0;i<k;i++)//与之前上过色的顶点进行比较 
	{
		if(side[k][i]==1&&color[i]==c)//相连的顶点上颜色相同 
		return false;
	}
	return true;
}

n位逐位整除数


#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;


void backtrack(int *a, int t, int n, int &sum)
{
    // 请在这里补充代码,完成本关任务
    /********* Begin *********/
    if(t==n+1){
        int y=0,m=0;
        for(int i=0;i<n;i++){
            y=(m*10+a[i])%n;
            m=y;
        }
        if(y==0){
            sum++;
        }
    }
    else{
        if(t==1){
            for(int i=1;i<10;i++){
                a[t-1]=i;
                backtrack(a, t+1, n, sum);
            }
        }
        else{
            int y=0,m=0;
            for(int i=0;i<t-1;i++){
                y=(m*10+a[i])%(t-1);
                m=y;
            }
            if(y==0){  
	          for(int i=0;i<10;i++){
	                a[t-1]=i;
	                backtrack(a,t+1,n,sum);
	          }
	        }
         }
    }
 
    /********* End *********/
}


int main(int argc, const char * argv[]) {
    
    int a[101];
    int n;
    scanf("%d", &n);
    
    int sum = 0;
    backtrack(a, 1, n, sum);

    printf("%d\n", sum);
    
    return 0;
}


素数圈

#include<stdio.h>
#include<math.h>
#include <stdbool.h>
int n;
int ans[110],used[110];

bool isPrime(int sum)   //判断sum是否是素数
{
    for(int i=2; i<=sqrt(sum); i++)
    {
        if(sum%i==0)
        {
            return false;
        }
    }
    return true;
}
 
bool isMeetA(int step)     //判断新填的第m个数字是否满足条件A
{
    if(step==1)    //只填了1个数字,满足条件A
    {
        return true;
    }
    else
    {
        if(isPrime(ans[step-1]+ans[step]))
        {
            if(step==n)
            {
                if(isPrime(ans[step]+ans[1])) //判断首尾数字加起来是否是素数
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return true;
            }
        }
        else
        {
            return false;
        }
    }
}

bool dfs(int step)
{
    if(step==n+1)
    {
    	printf("围成的圈是:"); 
        for(int i=1; i<=n; i++)
        {
            printf("%d ",ans[i]);
        }
        printf("\n");
        return true;
    }
    else
    {
        for(int i=1; i<n+1; i++)
        {
            if(!used[i])    //数字i还未被使用
            {
                used[i]=1;
                ans[step]=i;
                if(isMeetA(step))  //若ans[m]=i满足条件A才继续深搜
                {
                    if(dfs(step+1))    //若返回true证明已有一组解,不必再搜
                    {
                        return true;
                    }
                }
                used[i]=0;
                ans[step]=0;
            }
        }
        return false;
    }
}

int main()
{
	scanf("%d",&n);
    dfs(1);
    return 0;
}
 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值