算法设计与分析习题大全

算法设计与分析习题大全

By HB
题目来源:PTA

一、算法基础

  • 办事大厅排队(STL)
#include<iostream>
#include<string>
#include<list>
#include<stdio.h>
using namespace std;
int main()
{   
    int n,i;
    string str;
    list<string> data;
    cin>>n;   
    for(i=0;i<=n;i++)
    {
        getline(cin,str);        
        if(str[0]=='i'){
            data.push_back(str.substr(3,str.length()));           
        }
        if(str[0]=='o')
        {
            if(!data.empty())
                data.pop_front();
        }
       if(str[0]=='q')
       {
            if(!data.empty())
                cout<<data.front()<<endl;
            else
                cout<<"NULL"<<endl;
        }        
    }    
    return 0;
}
  • 利用STL比较数据大小并排序
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{   
    vector<int> arr;
    int n;
    while(cin>>n)
    {
        arr.push_back(n);
    }
    sort(arr.begin(),arr.end());
    cout<<"从标准设备读入数据,直到输入是非整型数据为止"<<endl;
    for(int i=0;i<arr.size();i++)
        cout<<" "<<arr.at(i);
    cout<<"\n";
    return 0;
}
  • 跳一跳
#include<iostream>
using namespace std;
int main()
{
    int n;
    int old = 1;
    int flag = 1;
    int sum =0;
    while(cin>>n)
    {
        if(n==0)break;
        if(n==2&&old==2)
            flag+=2;
        if(n==2&&old==1)
            flag=2;
        if(n==1)
            flag=1;
            old = n;
            sum+=flag;
    }
    cout<<sum<<endl;    
    return 0;
}
  • 排序
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
    vector<long long> numArr;
    int n;
    cin>>n;
    long long num;
    for(int i=0;i<n;i++)
    {
        cin>>num;
        numArr.push_back(num);
    }
    sort(numArr.begin(),numArr.end());
     for(int i=0;i<n;i++)
    {   
        if(i<n-1)
            cout<<numArr.at(i)<<" ";
        else
            cout<<numArr.at(i);
    }   
    return 0;
}
  • 求前缀表达式的值
#include<bits/stdc++.h> 
using namespace std;
float f()
{
   char A[10];
   cin>>A; 
   if(A[1]==NULL )
   {
        switch(A[0])
        {
            case'*': return f()*f();
            case'/':
                    {
                        float fenzi,fenmu;
                        fenmu=f();
                        fenzi=f();
                        if(fenzi==0)
                        {
                            cout << "ERROR"<<endl;
                            exit(0);
                        }
                        else return fenmu/fenzi;
                    }
            case'-': return f()-f();
            case'+': return f()+f();
            default: return atof(A);
        }
   }
   else
   {
        if(A[0]=='+' || A[0]=='-')
        {
            char flag=A[0];
            int i=0;
            while(A[i])
            {
                A[i]=A[i+1];
                i++;
            }
            if(flag=='-')
                return 0-atof(A);
            else
                return atof(A);
        }
        else return atof(A);
   }
};
int main( )
{
    float Sum;
    Sum=f();
    cout<<fixed<<setprecision(1)<<Sum<<endl;
    return 0;
}
  • 最大公约数(枚举and欧几里得)
#include <iostream>
#include<time.h>
using namespace std;
long long fun1(long long x,long long y)
{
    if(x>y)
    {
        x=x^y;
        y=x^y;
        x=x^y;
    }
    long long r = y%x;
    while(r!=0)
    {
        y=x;
        x=r;
        r=y%x;
    }
    return x;
}
long long fun2(long long x,long long y)
{
    if(x>y)
    {
        x=x^y;
        y=x^y;
        x=x^y;
    }
    for(long long i=x;i>=1;i--)
        if(y%i==0&&x%i==0)return i;
    return 0;
}
int main()
{
    clock_t startTime,endTime;
    long long x,y;
    cout<<"输入两个整数"<<endl;
    cin>>x>>y;
    startTime = clock();
    cout <<"穷举法:("<<x<<","<<y<<")最大公约数:"<<fun2(x,y) << endl;
    endTime = clock();
    cout<<"穷举运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    startTime = clock();
    cout <<"欧几里得法:("<<x<<","<<y<<")最大公约数:"<<fun1(x,y) << endl;
    endTime = clock();
    cout<<"欧几里得运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    return 0;
}
  • 排序算法效率比较
#include <iostream>
#include<time.h>
#include<stdlib.h>
using namespace std;
void display(int *arr,int n)
{
    for(int i=0;i<n;i++)
        cout<<arr[i]<<" ";
    cout<<"\n";
}
//数组拷贝
int *copyArr(int *arr,int n)
{
    int *num= (int *) malloc(sizeof(int)*(n+10));//数组初始化;
    for(int i=0;i<n;i++)
    {
        num[i]=arr[i];
    }
    return num;
}
//获得逆向数组
int *getReverseArr(int *arr,int n)
{
    int *num= (int *) malloc(sizeof(int)*(n+10));//数组初始化;
    for(int i=n-1,j=0;i>=0;i--,j++)
    {
        num[j]=arr[i];
    }
    return num;
}
//获得随机数组
void getRandomArr(int *arr,int n)
{
    for(int i=0;i<n;i++)
        arr[i]=rand()%n;//产生0-n的随机数
}
//冒泡排序
void miaoSort(int *arr,int n)
{
    for(int i=0;i<n;i++)
        for(int j=1;j<n-i;j++)
        {
            if(arr[j-1]>arr[j])
            {
                arr[j-1] = arr[j-1]^arr[j];
                arr[j] = arr[j-1]^arr[j];
                arr[j-1] = arr[j-1]^arr[j];
            }
        }
}
//选择排序
void xuanSort(int *arr,int n)
{
    for(int i=0;i<n;i++)
    {
        int index = i;
        for(int j=i+1;j<n;j++)
        {
            if(arr[index]>arr[j])
            {
                index = j;
            }
        }
         arr[index] = arr[i]^arr[index];
         arr[i] = arr[i]^arr[index];
         arr[index] = arr[i]^arr[index];
    }
}
//希尔排序
void sellSort(int *arr,int n)
{
    int step = n/5+1;//设置步长

    while(step)
    {
        for(int i=step;i<n;i++)
        {
            int num = arr[i];
            int j = i;
            while(j>=step&&num<arr[j-step])
            {
                arr[j]=arr[j-step];
                j=j-step;
            }
            arr[j]=num;
        }
        step/=5;//除以增量
    }
}
//快排非递归,因为逆序排列递归过深会造成溢出
int position(int *arr,int left,int right)
{
       if(left>=right)
    {
        return left;
    }
    int i,j,base;
    i = left,j = right;
    base = arr[left];
    while(i!=j)
    {

        while(arr[j]>=base&&i<j)
            j--;
        while(arr[i]<=base&&i<j)
            i++;
        if(i<j)
        {
            arr[i] = arr[i]^arr[j];
            arr[j] = arr[i]^arr[j];
            arr[i] = arr[i]^arr[j];
        }
    }
    arr[left] = arr[i];
    arr[i] = base;
    return i;
}
void quickSort(int *arr,int left,int right,int n)
{
    int *stackArr = (int*)malloc(sizeof(int)*10*n);
    int top = 0;
    int par = position(arr,left,right);
     //入栈
    if(par>left+1){
        stackArr[top++]=left;
        stackArr[top++]=par-1;
    }
    if(par<right-1){
        stackArr[top++]=par+1;
        stackArr[top++]=right;
    }
    while(top>0)
    {
        right=stackArr[--top];
        left=stackArr[--top];
        par=position(arr,left,right);
        if(par>left+1){
            stackArr[top++]=left;
            stackArr[top++]=par-1;
        }
        if(par<right-1){
            stackArr[top++]=par+1;
            stackArr[top++]=right;

        }
    }
}

int main()
{
    int *arrTemp;
    int n = 100000;
    int *arr=(int *) malloc(sizeof(int)*(n+10));//数组初始化
    clock_t startTime,endTime;
    srand((unsigned)time(NULL)); //随机数初始化
    getRandomArr(arr,n);

    cout<<"--------------冒泡排序------------------"<<endl;
    arrTemp = copyArr(arr,n);
    startTime = clock();
    miaoSort(arrTemp,n);
    endTime = clock();
    cout<<"数组大小n="<<n<<",算法运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    arrTemp = getReverseArr(arrTemp,n);
    startTime = clock();
    miaoSort(arrTemp,n);
    endTime = clock();
    cout<<"数组完全反序运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;


    cout<<"\n--------------选择排序------------------"<<endl;
    arrTemp = copyArr(arr,n);
    startTime = clock();
    xuanSort(arrTemp,n);
    endTime = clock();
    //display(arrTemp,20);
    cout<<"数组大小n="<<n<<",算法运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    arrTemp = getReverseArr(arrTemp,n);
    startTime = clock();
    xuanSort(arrTemp,n);
    endTime = clock();
    cout<<"数组完全反序运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;

    cout<<"\n--------------希尔排序------------------"<<endl;
    arrTemp = copyArr(arr,n);
    startTime = clock();
    sellSort(arrTemp,n);
    endTime = clock();
    //display(arrTemp,20);
    cout<<"数组大小n="<<n<<",算法运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    arrTemp = getReverseArr(arrTemp,n);
    startTime = clock();
    sellSort(arrTemp,n);
    endTime = clock();
    cout<<"数组完全反序运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;

    cout<<"\n--------------快速排序------------------"<<endl;
    arrTemp = copyArr(arr,n);
    startTime = clock();
    quickSort(arrTemp,0,n-1,n);
    endTime = clock();
    //display(arrTemp,20);
    cout<<"数组大小n="<<n<<",算法运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    arrTemp = getReverseArr(arrTemp,n);
    startTime = clock();
    quickSort(arrTemp,0,n-1,n);
    endTime = clock();
    cout<<"数组完全反序运行时间:"<<((double)(endTime-startTime)/CLOCKS_PER_SEC)<<"s"<<endl;
    return 0;
}

二、枚举

  • 搬砖(穷举问题)
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    int flag =0;
    for(int i = 0;i<=n;i++)
    {
        for(int j=0;j<=n-i;j++)
        {
            if((3*i+2*j+int((n-i-j)/2))==n&&(n-i-j)%2==0)
            {
                printf("men = %d, women = %d, child = %d\n",i,j,(n-i-j));
                flag=1;
            }        
        }
    }
    if(flag==0)cout<<"None"<<endl;
}

  • 百鸡问题扩展-N鸡问题
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
    int n;
    int sum1 = 0;
    int sum2 = 0;
    cin>>n;
    for(int i = 0;i<=n/5;i++)
    {
        for(int j=0;j<=n/3;j++)
        {
            if((i+j+(n-5*i-3*j)*3)==n)
            {
                sum1++;
                sum2+=i;               
            }         
        }
    }
    if(sum1==0)cout<<"0 -1"<<endl;
    else
        cout<<sum1<<" "<<sum2<<endl;
}
  • 输出全排列(STL)
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    string str="";
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        str+=string(1,('0'+i));
    }
    do
    {
        cout<<str<<endl;
    }while(next_permutation(str.begin(),str.end()));
    return 0;
}
  • 梅森数
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
bool isPrime(int num)
{
    if(num%2==0) return false;    
    for(int i=3;i<=num/2;i++)
        if(num%i==0)return false;    
    return true;
}
int main()
{
    string str="";
    int n;
    cin>>n;
    int num=2;
    int flag = 0;
    for(int i=1;i<n;i++)
    {
       num=2<<i;
       if(isPrime(num-1))
       {
           cout<<num-1<<endl;
           flag =1;
       }        
    }
    if(!flag)cout<<"None"<<endl;
    return 0;
}
  • 换硬币
#include<algorithm>
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    int count=0;
    for(int i=n/5;i>=1;i--)
    {
        for(int j=n/2;j>=1;j--)
        {
            
            if((5*i+2*j+(n-5*i-2*j))==n&&(n-5*i-2*j)>=1)
            {
                printf("fen5:%d, fen2:%d, fen1:%d, total:%d\n",i,j,(n-5*i-2*j),(n-5*i-2*j+i+j));
                count++;
            }
        }
    }
    cout<<"count = "<<count<<endl;
    return 0;
}
  • 由0到4五个数字,组成5位数,每个数字用一次,但十位和百位不能为3(当然万位不能为0),输出所有可能的五位数
import java.lang.reflect.Array;
import java.util.*;
public class Main {
    public static boolean check(int n)
    {
        if((n/10000)!=0&&(n/10)%10!=3&&(n/100)%10!=3)
        {
            int num[] = new int[5];
            for(int i=4;i>=0;i--)
                num[i] = (int) (n/Math.pow(10,i))%10;
            for(int i=0;i<5;i++)
                for(int j=0;j<5;j++)
                    if(i!=j&&num[i]==num[j])return false;
            return true;
        }
        return false;
    }
    public static void main(String[] args) {
        for(int n1=1;n1<=9;n1++)
            for(int n2=0;n2<=9;n2++)
                for(int n3=0;n3<=9;n3++)
                    for(int n4=0;n4<=9;n4++)
                        for(int n5=0;n5<=9;n5++)
                        {
                            int num = n1*10000+n2*1000+n3*100+n4*10+n5;
                            if(check(num))System.out.println(num);
                        }
    }
}
  • 最大子段和问题。给定由n个整数组成的序列,求序列中子段的最大和,若所有整数均为负整数时定义最大子段和为0。例如, 当(a1,a2,a3,a4 ,a5,a6) = (-2,11,-4,13,-5,-2)时,最大子段和为: a2+a3+a4=20
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n =sc.nextInt();
        int num[] = new int[10002];
        int maxNum =0;
        int x=0;
        int y=0;
        boolean flag=false;
        for(int i=0;i<n;i++)
        {   
            num[i] =sc.nextInt();
            if(num[i]>=0)flag=true;
        }
        for(int i=0;i<n;i++)
            for(int j=0;(j+i)<n;j++)
            {   int sum =0;
                for(int k=i;k<(j+i);k++)
                    sum+=num[k];
                if(sum>maxNum)
                {
                    maxNum = sum;
                    x = i+1;
                    y = i+j;
                }
            }
        if(flag) {
            System.out.println(maxNum);
            System.out.println(x + " " + y);
        }
        else
         System.out.println(0);
    }
}
  • 有两队选手每队5人进行一对一的比赛,甲队为A、B、C、D、E,乙队为J、K、L、M、N,经过抽签决定比赛对手名单。规定A不和J比赛, M不和D及E比赛。列出所有可能的比赛名单。
import java.util.HashMap;
import java.util.Map;
public class Main {
    static char[] team1 = {'A','B','C','D','E'};
    static char[] team2 = {'J','K','L','M','N'};
    public static String check(char[] t1,char[] t2,char[] t3,char[] t4,char[] t5)
    {
        String str ="";
        if(t4[1]!='M'&&t5[1]!='M')
        {
            if(t1[1]!='J')
            {
                Map<Character, Integer> m = new HashMap<Character, Integer>();
                m.put(t1[1],1);
                m.put(t2[1],1);
                m.put(t3[1],1);
                m.put(t4[1],1);
                m.put(t5[1],1);
                for(char c:team2)
                    if(!m.containsKey(c))return "";
                str+=(""+t1[0]+t1[1]+" "+t2[0]+t2[1]+" "+t3[0]+t3[1]+" "+t4[0]+t4[1]+" "+t5[0]+t5[1]+" ");
                return str;
            }
        }
        return "";
    }
    public static void main(String args[])
    {
        char teamR[][][] = new char[5][5][2];
            for (int i = 0; i < 5; i++) {
                for (int j = 0; j < 5; j++) {
                    teamR[j][i][0] = team1[i];
                    teamR[j][i][1] = team2[j];
                }
            }

        System.out.println("所有比赛方法数:");
        int sum =0;
        for(int i1=0;i1<5;i1++)
            for(int i2=0;i2<5;i2++)
                for(int i3=0;i3<5;i3++)
                    for(int i4=0;i4<5;i4++)
                        for(int i5=0;i5<5;i5++)
                        {
                            String str="";
                            if((str=check(teamR[i1][0],teamR[i2][1],teamR[i3][2],teamR[i4][3],teamR[i5][4]))!="")
                            {
                                    System.out.println(str);
                                    sum++;
                            }
                        }
        System.out.println("共个"+sum+"比赛方法:");
    }
}
  • 3n+1最高点
import java.util.Scanner;
public class AlgorithmTest1 {
   public static void main(String args[])
   {
        Scanner sc = new Scanner( System.in);
        long n = sc.nextLong();
        long maxNum = 0;
        long num = 0;
        for(int i=2;i<=n;i++)
        {
            num = i;
            while(num!=1)
            {
                if(num%2==0)num/=2;
                else num=3*num+1;       
                if(num>maxNum)
                {
                    maxNum = num;
                }
            }
        }
        System.out.println(maxNum); 
   }
}

三、递归

  • 递归实现逆序输出整数
#include<iostream>
using namespace std;
void f(int n)
{
    cout<<n%10;
    if(n/10!=0)
        f(n/10);    
}
int main()
{
    int n;
    cin>>n;
    f(n);
    cout<<"\n";
    return 0;
}
  • 二分查找
#include<iostream>
#include<algorithm>
using namespace std;
int findNum(int k,int left,int right,int *arr)
{   
    int step = 1;
    int n = (right-left)/2+left; 
    if(arr[n]==k)
    {
      cout<<n<<endl;
      return step;  
    }
    if(left==right){
        cout<<-1<<endl;
        return step;
    }
    if(arr[n]<k)left = n+1;
    else right = n-1;
    step += findNum(k,left,right,arr);
    return step;
}
int main()
{
    int n;
    int arr[1001];
    int k;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>arr[i];
    cin>>k;
    sort(arr,arr+n);
    int step = findNum(k,0,n-1,arr);
    cout<<step<<endl;
    return 0;
}
  • 改写二分搜索算法
#include<iostream>
#include<algorithm>
using namespace std;
int findNum(int k,int left,int right,int *arr)
{   
    int step = 1;
    int n = (right-left)/2+left;
    if(arr[n]==k)
    {
      cout<<n<<" "<<n<<endl;
      return step;
    }
    if(left>=right){
        if(n==0)cout<<n-1<<" "<<n<<endl;
        else
            cout<<n<<" "<<n+1<<endl;
        return step;
    }
    if(arr[n]<k)left = n+1;
    else right = n-1;
    step += findNum(k,left,right,arr);
    return step;
}
int main()
{
    int n;
    int arr[1001];
    int k;
    cin>>n>>k;
    for(int i=0;i<n;i++)
        cin>>arr[i];
    sort(arr,arr+n);
    int step = findNum(k,0,n-1,arr);
    return 0;
}
  • 分形的递归输出
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<string.h>
using namespace std;
char dp[1001][1001];
void solve(int n,int x, int y)
{
    if(n==0)
    {
        dp[x][y]='X';
        return;
    }
    int len = int(pow(3,n-1));
    solve(n-1,x,y);
    solve(n-1,x,y+2*len);
    x = x+len;
    solve(n-1,x,y+len);
    x = x+len;
    solve(n-1,x,y);
    solve(n-1,x,y+2*len);
}
int main()
{
    int n;
    memset(dp,32,sizeof(dp));
    while(scanf("%d",&n) != EOF)
    {
        if(n==-1)break;
        if(dp[int(pow(3,n-1)-1)][int(pow(3,n-1)-1)]!='X')
            solve(n-1,0,0);
        for(int i=0;i<int(pow(3,n-1));i++)
        {
           for(int j=0;j<int(pow(3,n-1));j++)
            {
               if(dp[i][j]=='X')
                  cout<<"X";
               else
                   cout<<" ";
            }
            printf("\n");
        }
            printf("-\n");
    }
    return 0;
}
  • 棋盘覆盖
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<iomanip>
using namespace std;
int dp[2026][2026];///棋盘最大2^k*2^k ,k=10
int v[4][2]={0,0,1,0,1,1,0,1};///四个格子基于原点的坐标
//深搜递归分治
/**
*parm
*x,y棋盘左上角坐标
*aa,ay 特殊点坐标
*k 棋盘规模 2^k*2^k
*/
int step=1;
void dfs(int x,int y,int ax,int ay,int k)
{
    int mx = x+int(pow(2,k)/2)-1;
    int my = y+int(pow(2,k)/2)-1;//找出中间点坐标
    if(k==1)
    {
        for(int i=0;i<4;i++)
        {
            int tx = x+v[i][0];
            int ty = y+v[i][1];
            if(tx!=ax||ty!=ay)dp[ty][tx]=step;//填充格子
        }
        step++;
        return;
    }
    int flag = 0;//特殊点所在的棋盘
    if(ax<=mx&&ay<=my)flag=0;//特殊点在左上棋盘
    if(ax>=(mx+1)&&ay<=my)flag=1;//特殊点在右上棋盘
    if(ax>=(mx+1)&&ay>=(my+1))flag=2;//特殊点在右下棋盘
    if(ax<=mx&&ay>=(my+1))flag=3;//特殊点在左下棋盘

    for(int i=0;i<4;i++)
            if(i!=flag)dp[my+v[i][1]][mx+v[i][0]]=step;//填充非特殊点的棋盘
    step++;
    for(int i=0;i<4;i++)
    {
        //找新棋盘的原点坐标
        int tx = mx+v[i][0];
        int ty = my+v[i][1];
        int ox = tx;
        int oy = ty;
        if(i==0)
        {
            ox = ox-pow(2,k-1)+1;
            oy = oy-pow(2,k-1)+1;
        }
        if(i==1)
        {
            oy = oy-pow(2,k-1)+1;
        }
        if(i==3)
        {
            ox = ox-pow(2,k-1)+1;
        }
        if(i!=flag)
            dfs(ox,oy,tx,ty,k-1);//非特殊点的棋盘
        else
            dfs(ox,oy,ax,ay,k-1);//特殊点的棋盘
    }
}
int main()
{
    int ax,ay,len;
    cin>>ax>>ay>>len;
    int k = 0;
    while(len>>=1)k++;
    dfs(0,0,ay-1,ax-1,k);
    for(int i=0;i<pow(2,k);i++)
    {
        for(int j=0;j<pow(2,k);j++)
            printf("%4d",dp[i][j]);
        cout<<"\n";
    }
    return 0;
}
  • 循环日程表
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
/*
*数独问题
*/
int dp[1024][1024];
int check(int x,int y,int n,int m)
{
    for(int i=0;i<m;i++)
        if(dp[x][i]==n||dp[i][y]==n)return 0;    
    return 1;
}
void solve(int n,int m,int x,int y)
{
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            for(int k=1;k<=m;k++)
                if(check(i,j,k,m))
                {
                    dp[i][j]=k;
                    break;
                }    
}
int main()
{   
    int n;
    cin>>n;
    int m = int(pow(2,n));
    solve(1,m,0,0);
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<m;j++)
            cout<<dp[i][j]<<" ";
        cout<<"\n";
    }
    return 0;
}
  • Fibonacci数列
/*
* 斐波那契练习,递归和尾递归
* */
public class Main {
    public static long fibonacci(int n)
    {
        //递归
        if(n==0)return 0;
        if(n==1)return 1;
        return fibonacci(n-1)+fibonacci(n-2);
    }
    public static long tailFibonacci(int n,long n1,long n2)
    {
        if(n==0) return n1;
        return tailFibonacci(n-1,n2,n1+n2);
    }
    public static void main(String[] args) {
        long startTime ;
        long endTime;
        for(int i=0;i<6;i++)
        {
            startTime = System.currentTimeMillis();
            long num = fibonacci(i+47);
            endTime = System.currentTimeMillis();
            System.out.println("斐波那契递归算法计算:"+(i+47)+":"+num+" 所需时间:"+(endTime-startTime)+"ms");
        }
        for(int i=0;i<6;i++)
        {
            startTime = System.currentTimeMillis();
            long num = tailFibonacci(i+47,0,1);
            endTime = System.currentTimeMillis();
            System.out.println("斐波那契尾递归算法计算:"+(i+47)+":"+num+" 所需时间:"+(endTime-startTime)+"ms");
        }
    }
}
  • 3n+1问题
public class Main {
    static int solve(int n)
    {
        int count =0;
        while(n!=1)
        {
            if(n%2==0)n/=2;
            else n= 3*n+1;
            count++;
        }
        return count;
    }
    public static void main(String[] args) {
        int n=100;
        System.out.println(n+" 计算到1所需步数"+solve(n));
    }
}
  • n级台阶,一次可上一阶或两阶求所有方法数
public class AlgorithmTest7 {
    public static long solve(int n)
    {
        //递归方法
        if(n==0)return 1;
        if(n<0)return 0;
        return solve(n-1)+solve(n-2);
    }
    public static void main(String[] args) {
        long startTime;
        long endTime;
        int n=50;
        startTime = System.currentTimeMillis();
        long num = solve(n);
        endTime = System.currentTimeMillis();
        System.out.println(n+"台阶方法数:"+num+",递归算法计算所需时间:"+(endTime-startTime)+"ms");
    }
}
  • 半数集问题
public class Main {
    public static int solve(int n)
    {
        if(n==0)return 1;
        int sum = 0;
            for(int i=0;i<=(n/2);i++)
                sum+=solve(i);
        return sum;
    }
    public static void main(String[] args) {
        int n = 6;
        System.out.println("set("+n+")半数集元素个数"+solve(n));
    }
}
/*
* 半数集问题
* +改进方法
* */
public class AlgorithmTest8 {
    public static long [] num = new long[2001];
    public static long solve(int n)
    {
        if(n==0)return 1;
        long sum = 0;
        for(int i=0;i<=(n/2);i++)
            sum+=solve(i);
        return sum;
    }
    public static long solve2(int n)
    {
        if (num[n] <= 0) {
            num[n] = 1;
            for (int i = 1; i <= (n / 2); i++)
                num[n] += solve(i);
        }
        return num[n];//直接返回避免重复计算
    }
    public static void main(String[] args) {
        long startTime ;
        long endTime;
        int n=1300;
        startTime = System.currentTimeMillis();
        long num = solve(n);
        endTime = System.currentTimeMillis();
        System.out.println("set("+n+")半数集元素个数"+num+",运行时间"+(endTime-startTime)+"ms");
        startTime = System.currentTimeMillis();
        num = solve2(n);
        endTime = System.currentTimeMillis();
        System.out.println("set("+n+")半数集元素个数"+num+",优化算法运行时间"+(endTime-startTime)+"ms");
    }
}

四、贪心算法

  • 装箱问题
#include <iostream>
#include<string.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    char box[1001];
    memset(box,100,sizeof(box));
    int len = 1;
    while(n--)
    {
        int goodsW;
        cin>>goodsW;
        int index = 1;
        while(box[index]<goodsW)index++;
        box[index]-=goodsW;
        if(index>len)len=index;
        cout<<goodsW<<" "<<index<<endl;
    }
    cout<<len<<endl;
    return 0;
}
  • 月饼
#include <iostream>
#include<algorithm>
#include <iomanip>
#include<stdio.h>
using namespace std;
struct monCake{
    int w;//总库存
    int t;//总售价
    double p;//单位利润
}monCakeArr[1050];
bool cmp(monCake m1,monCake m2)
{
    return m1.p>m2.p;
}
int main()
{
    int n,volume;//种类和需求量
    int i=0;
    cin>>n>>volume;
    for(i=0;i<n;i++)
    {
        int w;
        cin>>w;
        monCakeArr[i].w=w;
    }
    for(i=0;i<n;i++)
    {
        int t;
        cin>>t;
        monCakeArr[i].t=t;
        monCakeArr[i].p=monCakeArr[i].t*1.0/monCakeArr[i].w*1.0;
    }
    sort(monCakeArr,monCakeArr+n,cmp);
    double sum=0;
    for(i=0;i<n&&volume>0;i++)
    {
        if(monCakeArr[i].w<=volume){
            sum+=monCakeArr[i].t;
            volume-=monCakeArr[i].w;
        }
        else
        {
            sum+=(monCakeArr[i].p*volume);
            volume=0;
        }
    }
    printf("%.2f",sum);
    return 0;
}
  • 最优合并问题

给定k 个排好序的序列, 用 2 路合并算法将这k 个序列合并成一个序列。 假设所采用的 2 路合并算法合并 2 个长度分别为m和n的序列需要m+n-1 次比较。试设 计一个算法确定合并这个序列的最优合并顺序,使所需的总比较次数最少。 为了进行比较,还需要确定合并这个序列的最差合并顺序,使所需的总比较次数最多。

#include <iostream>
#include<algorithm>
using namespace std;
//m和n合并需要 m+n-1次
bool cmp(int a,int b)
{
    return b<a;
}
int getMaxNum(int *arr,int n)
{   
    //获取最多比较次数
    int num=0;
    sort(arr,arr+n,cmp);//降序
    for(int i=0;i<n-1;i++)
    {
        num+=(arr[i]+arr[i+1]-1);
        arr[i+1] = arr[i]+arr[i+1];
        sort(arr+i+1,arr+n,cmp);
    }
    return num;
}
int getMinNum(int *arr,int n)
{   
    //获取最少比较次数
    int num=0;
    sort(arr,arr+n);//升序
    for(int i=0;i<n-1;i++)
    {
        num+=(arr[i]+arr[i+1]-1);
        arr[i+1] = arr[i]+arr[i+1];
        sort(arr+i+1,arr+n);
    }
    return num;
}
int main()
{
    int n,t;
    int arr1[10000];
    int arr2[10000];
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>t;
        arr1[i]=t;
        arr2[i]=t;//两份数组用于升降序
    }
    cout<<getMaxNum(arr1,n)<<" "<<getMinNum(arr2,n)<<endl;;
    return 0;
}
  • 看电影
#include <iostream>
#include<algorithm>
using namespace std;
struct timeT{
    int s;
    int e;
}timeTArr[102];
bool cmp(timeT t1,timeT t2)
{
    return t1.e<t2.e;//按结束时间由低到高排序
}
int main()
{   
    int n;
    int len =24;
    while(cin>>n)
    {
        if(n==0)break;
        int i=0;
        for(i=0;i<n;i++){
            int s,e;
            cin>>s>>e;
            timeTArr[i].s=s;
            timeTArr[i].e=e;
        }
        sort(timeTArr,timeTArr+n,cmp);
        int sum=0;
        int e=0;
        for(i=0;i<n;i++)
        {
            if(timeTArr[i].s>=e){
                e=timeTArr[i].e;
                sum++;
            }
        }
        cout<<sum<<endl;
    }
    return 0;
}
  • 喷水装置
#include <iostream>
#include<math.h>
#include<algorithm>
using namespace std;
struct huasa{
    int p;//圆心
    double x;//头
    double y;//尾
}arr[20006];
int main()
{
   int n;
   cin>>n;
   while(n--)
   {
       int m,l,w;
       cin>>m>>l>>w;
       int cnt=0;
       for(int i=0;i<m;i++)
       {
           int p,r;
           cin>>p>>r;
           if(2 * r <= w)
               continue;
           arr[cnt].x = p-sqrt((r*r-w*w/4.0));
           arr[cnt].y = p+sqrt((r*r-w*w/4.0));
           cnt++;
       }
       double e = 0;
       int flag = 1;
       int sum = 0;
       while(e<l)
       {
           sum++;
           double tempE=e;//新的末端
           for(int j=0;j<cnt;j++)
           {
                if(arr[j].x<=tempE&&arr[j].y>e)e=arr[j].y;
           }
           if(tempE==e&&tempE<l){
                cout<<-1<<endl;
                flag = 0;break;//无法覆盖退出
           }
       }
       if(flag)
            cout<<sum<<endl;
   }
    return 0;
}
  • 活动选择问题
#include <iostream>
#include<algorithm>
using namespace std;
struct activity
{
    int s;
    int e;
} arr[1002];
bool cmp(activity a1,activity a2)
{
    return a1.e<a2.e;
}
int main()
{
    int n;
    cin>>n;
    int len=0;
    while(n--)
    {
        int s;
        int e;
        cin>>s>>e;
        arr[len].s=s;
        arr[len++].e=e;
    }    
    sort(arr,arr+len,cmp);
    int sum=0;
    int e=0;
    for(int i=0;i<len;i++)
        if(arr[i].s>=e){e=arr[i].e;sum++;}
    cout<<sum<<endl;
    return 0;
}
  • 最优服务次序问题
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class Main {
    /**
     *问题描述:设有n个顾客同时等待一项服务,顾客i需要的服务时间为ti,(1<=i<=n)。
     *应如何安排n个顾客的服务次序才能使平均等待时间达到最小?
     *(平均等待时间是n个顾客等待服务时间总和除以n)
     * 贪心按服务时间从小到 大排序
     **/
    public static void main(String[] args) {
        Scanner sc =  new Scanner( System.in);
        int n = sc.nextInt();
        Integer[] arr = new Integer[n];
        for(int i=0;i<n;i++)
        {
            arr[i]=sc.nextInt();
        }
        Arrays.sort(arr);
        double avg = 0;
        long sum = 0;
        long k=0;
        System.out.println("服务顺序:");
        for(Integer i:arr)
        {
            System.out.print(i+" ");
            sum+=k;
            k+=i;
        }
        System.out.println("\n平均服务时间:");
        System.out.println((double)(sum/n*1.0));
    }
}
  • 多处最优服务次序问题
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class Main {
    /**
     *问题描述:设有n个顾客同时等待一项服务,顾客i需要的服务时间为ti,(1<=i<=n)。
     *共有s处可以提供此项服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?
     * 多处最优问题,贪心按服务时间从小到 大排序,然后按序分配给每个服务点
     **/
    public static void main(String[] args) {
        Scanner sc =  new Scanner(System.in);
        int n =sc.nextInt();
        int s = sc.nextInt();//s个服务点
        Integer[] arr = new Integer[n];
        Integer[] arrK = new Integer[s];//s个服务点服务时间计数
        Arrays.fill(arrK,0);
        for(int i=0;i<n;i++)
        {
            arr[i]=sc.nextInt();
        }
        Arrays.sort(arr);
        double avg = 0;
        double sum = 0;
        System.out.println("服务顺序(服务点,服务时间):");
        int j=0;
        for(Integer i:arr)
        {
            System.out.print("("+j+","+i+") ");
            sum+=arrK[j];
            arrK[j]+=i;
            j++;
            j%=s;//一次循环返回第一个服务点
        }
        System.out.println("\n平均服务时间:");
        System.out.println((double)(sum*1.0/n*1.0));
    }
}
  • 最优分解问题
import java.util.Scanner;

public class Main {
    /*
    *问题描述:设n是一个正整数,要求将n分解为若干互不相同的自然数之和,且这些自然数的乘积最大。
    *输入:正整数n
    *输出:计算的最大乘积。
    *如输入10,则输出30.
    *提示:若a+b=const ,则a-b的绝对值越小,ab值越大。贪心策略:将n分成从2开始的连续自然数之和,如果最后剩下一个数,则将此数在后项优先的方式下均匀地分给前面各项。
    * */
    public static int solve(int n)
    {
        int result = 1;
        int num =n;
        int i=2;
        //a-b最小 ab乘积最大
        for(i=2;i<=n;i++)
            if(i<=num)num-=i;
            else break;
        int endNum = --i;
        while(endNum>=2)
        {
            int nowNum = endNum;
            endNum--;
            if(num>0){nowNum+=1;num--;}//均已分配给后项
            System.out.print(nowNum+" ");
            result*=nowNum;
        }
        System.out.println();
        return result;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n =sc.nextInt();
        System.out.println("最优分解乘积为:"+solve(n));
    }
}
  • 多机调度问题
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class AlgorithmTest12 {
    /*利用贪心法设计算法求解如下问题:
    *要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。作业不能拆分成更小的子作业。
    *这个问题是一个NP完全问题,到目前为止还没有一个有效的解法。对于这一类问题,用贪心选择策略有时可以设计出较好的近似算法。
    *可以考虑以下的贪心策略:
    *(1)最长处理时间作业优先的贪心选择策略。
    *(2)最短处理时间作业优先的贪心选择策略。
    *(3)作业到达时间优先的贪心选择策略。
    *假设7个独立的作业由3台机器加工处理,各作业所需的处理时间为:{2,14,4,6,16,5,3},写出以上算法求解此问题的结果。
    * */
    static class MyCmp implements Comparator<Integer>
    {
        @Override
        public int compare(Integer o1,Integer o2)
        {
            if(o1 < o2) {
               return 1;
           }else if(o1 > o2) {
               return -1;
           }else {
               return 0;
           }
        };
    }
    public static void longJopFirst(Integer [] arr,int resourceNum)
    {
        int sumTime[] = new int[resourceNum];
        int maxTime = 0;
        Arrays.sort(arr,new MyCmp());//降序
        System.out.println("长作业优先,执行顺序(机器ID,任务耗时)");
        for(int i=0;i<arr.length;i++)
        {   int minTimeIndex = 0;
            for (int j=0;j<resourceNum;j++)
                if(sumTime[j]<sumTime[minTimeIndex])minTimeIndex=j;//找到空闲的机器
            sumTime[minTimeIndex] += arr[i];
            if(sumTime[minTimeIndex]>maxTime) maxTime = sumTime[minTimeIndex];//存储作业时间
            System.out.print("("+minTimeIndex+","+arr[i]+") ");
        }
        System.out.println("\n作业时间"+maxTime);
    }
    public static void shortJopFirst(Integer [] arr,int resourceNum)
    {
        int[] sumTime = new int[resourceNum];
        int maxTime = 0;
        Arrays.sort(arr);//升序
        System.out.println("短作业优先,执行顺序(机器ID,任务耗时)");
        for(int i=0;i<arr.length;i++)
        {   int minTimeIndex = 0;
            for (int j=0;j<resourceNum;j++)
                if(sumTime[j]<sumTime[minTimeIndex])minTimeIndex=j;//找到空闲的机器
            sumTime[minTimeIndex] += arr[i];
            if(sumTime[minTimeIndex]>maxTime) maxTime = sumTime[minTimeIndex];//存储作业时间
            System.out.print("("+minTimeIndex+","+arr[i]+") ");
        }
        System.out.println("\n作业时间"+maxTime);
    }
    public static void firstComeFirstSolve(Integer [] arr,int resourceNum)
    {
        int[] sumTime = new int[resourceNum];
        int maxTime = 0;
        System.out.println("先来先服务,执行顺序(机器ID,任务耗时)");
        for(int i=0;i<arr.length;i++)
        {   int minTimeIndex = 0;
            for (int j=0;j<resourceNum;j++)
                if(sumTime[j]<sumTime[minTimeIndex])minTimeIndex=j;//找到空闲的机器
            sumTime[minTimeIndex] += arr[i];
            if(sumTime[minTimeIndex]>maxTime) maxTime = sumTime[minTimeIndex];//存储作业时间
            System.out.print("("+minTimeIndex+","+arr[i]+") ");
        }
        System.out.println("\n作业时间"+maxTime);
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("作业个数:");
        int n = sc.nextInt();
        System.out.print("机器个数:");
        int m = sc.nextInt();
        System.out.print("输入作业:");
        Integer arr[] = new Integer[n];
        for (int i=0;i<n;i++)
            arr[i]=sc.nextInt();
        //先来先服务,先执行,arr为浅拷贝
        firstComeFirstSolve(arr,m);
        //短作业优先
        shortJopFirst(arr,m);
        //长作业优先
        longJopFirst(arr,m);
    }
}

五、回溯法

  • 最佳调度问题
import java.util.Arrays;
import java.util.Scanner;
public class Main {
    /*最佳调度问题
     *假设有n(n<=20)个任务由k(k<=20)个可并行工作的机器完成。完成任务i需要的时间为ti。
     * 试设计一个算法,对任意给定的整数n和k,以及完成任务i 需要的时间为ti ,i=1~n。
     * 计算完成这n个任务的最佳调度,使得完成全部任务的时间最早。
     * 不能使用贪心算法
     * 使用回溯遍历解空间树剪枝
     * */
    private static Integer []arr = new Integer[22];
    private static Integer []resArr = new Integer[22];
    private static Integer minTime = 999999999;
    public static void dfs(int k,int n,int resourceNum)
    {

        if(k>=n)
        {
            //执行完所有任务
            int maxTime = 0;
            for(int i=0;i<resourceNum;i++)
                if(resArr[i]>maxTime)maxTime = resArr[i];//找出此解所需时间

            if(maxTime<minTime)minTime = maxTime;//如果小于最优解则替换
        }
        else
        {
            //还有任务未执行
            for(int i = 0;i<resourceNum;i++)//枚举每个资源
            {
                if((resArr[i]+arr[k])>minTime)continue;//剪枝掉大于最优解的分支
                resArr[i]+=arr[k];
                dfs(k+1,n,resourceNum);
                resArr[i]-=arr[k];//回溯
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        for(int i=0;i<resArr.length;i++)
            resArr[i]=0;//初始化数组
        for (int i=0;i<n;i++)
            arr[i]=sc.nextInt();
        dfs(0,n,m);
        System.out.println(minTime);
    }
}
  • 八皇后问题
#include <iostream>
using namespace std;
/**
*现在我们把棋盘扩展到 n×n 的棋盘上摆放 n 个皇后,请问该怎么摆?
请编写程序,输入正整数 n,输出全部摆法(棋盘格子空白处显示句点“.”,皇后处显示字母“Q”,每两个字符之间空一格)。
*/
int dp[2010][2010];
int sum = 0;
bool check(int y,int x,int n)
{
   for(int i=1;i<y;i++){
		if(dp[i][x]==1){
			return false;//列不同
		}
	}
	int i,j;
	for(i=y-1,j=x-1;i!=0&&j!=0;i--,j--){
		if(dp[i][j]==1){
			return false;//主对角
		}
	}
	for(i=y-1,j=x+1;i!=0&&j!=n+1;i--,j++){
		if(dp[i][j]==1){
			return false;//副对角
		}
	}
    return true;
}
void display(int n)
{
    sum++;
    if(sum>1)cout<<"\n";
    for(int i=1;i<=n;i++)
    {
         for(int j=1;j<=n;j++)
        {
            if(dp[i][j]==1)cout<<"Q";
            else cout<<".";

             if(j!=n)cout<<" ";

        }
        cout<<"\n";
    }
}
void dfs(int k,int n)
{
    if(k>n)
    {
        //放置完成
        display(n);
        return ;
    }
    else
    {
        for(int i=1;i<=n;i++)
        {           
            dp[k][i]=1;
            if(check(k,i,n))dfs(k+1,n);
            dp[k][i]=0;
        }
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=0;
    dfs(1,n);
    if(sum==0)
        cout<<"None"<<endl;
    return 0;
}
  • 0-1背包
#include<iostream>
#include<algorithm>
 //回溯超时是什么鬼???为什么要发在这里
using namespace std;
const int maxn=110;
const int maxv=1010;
int n,V;
int dp[maxv]={0},w[maxn],c[maxn];
int main(){	
	cin>>n>>V;	
	for(int i=1;i<=n;i++)
	   cin>>w[i]>>c[i];  
	for(int i=1;i<=n;i++){
		
		for(int v=V;v>=w[i];v--){
			
			dp[v]=max(dp[v],dp[v-w[i]]+c[i]);
		}
	}  	
	int max=-1;
	for(int i=0;i<=V;i++){
		
		if(max<dp[i])
		 max=dp[i];
	}
	cout<<max<<endl;	
}
  • 整数拆分
#include <iostream>
using namespace std;
int dp[1001]={1};
int n=0;
int sum =0;
void dfs(int num,int k)
{

    if(num==0)
    {
        cout<<n<<" = ";
        for(int i=1;i<k-1;i++)
            cout<<dp[i]<<" + ";
        cout<<dp[k-1]<<endl;
        sum++;
        return ;
    }
    else
    {
        for(int i=dp[k-1];i<=num;i++)
            if(i<n)
            {
                dp[k]=i;
                num-=i;
                dfs(num,k+1);
                num+=i;
            }
    }
}
int main()
{
    cin>>n;
    dfs(n,1);
    cout<<sum<<endl;
    return 0;
}
  • 666

小明有一张m*n的好习惯记录卡,记录每一天的好习惯目标达成度(数字0-9表示)。某天目标完成达成,就在当天的格子里写上数字6,目标没有完全达成就写上一个小于6的数字(0-5),目标超额完成就写上一个大于6的数字(7-9)。记录卡上如果能找到一条长度为3的路径并且路径上的三个数字都大于等于6(这里的路径是指从某个格子出发,可以向左、右、上、下格子移动,并且不能重复经过一个格子),则小明就能得到一个“666”奖励。请你帮小明统计下他总共能得到多少“666”奖励

#include <iostream>
//这题用java超时????,没有爱了T_T
using namespace std;

int v[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};//上、右,下,左
int numS[102][102];
int dp[102][102];
int arr[102][102];
int sum =0;
int dfs(int y,int x,int m,int n,int step)
{
    if(step>=3)
    {
        return 1;
    }else
    {
        int count = 0;
        for(int i=0;i<4;i++)
        {
            int ny = y+v[i][0];
            int nx = x+v[i][1];

            if(nx>=0&&ny>=0&&nx<n&&ny<m&&dp[ny][nx]!=1)
            {
                dp[ny][nx]=1;
                if(arr[ny][nx]>=6)
                {
                    count+=dfs(ny,nx,m,n,step+1);
                }
                dp[ny][nx]=0;
            }

        }
        return count;
    }
}
int main() {
    int m;
    int n;
    cin>>m>>n;
    for(int i =0;i<m;i++)
        for(int j=0;j<n;j++)
            cin>>arr[i][j];
    for(int i =0;i<m;i++)
        for(int j=0;j<n;j++)
        {
            dp[i][j]=1;
            int num = 0;
            if(arr[i][j]>=6)num = dfs(i,j,m,n,1);
            dp[i][j]=0;
            numS[i][j]=num;
            sum+=num;
            cout<<num<<((j==n-1)?"\n":" ");
        }
    cout<<sum<<endl;
    return 0;
}
  • 工作分配问题
#include <iostream>
using namespace std;
/***
设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为cij 。
设计一个算法,对于给定的工作费用,为每一个人都分配1 件不同的工作,并使总费用达到最小。
*/
int arr[22][22];
int minCost = 9999;
int sumCost = 0;
int vis[22];//工人个数
void dfs(int k,int n)
{
    if(k==n)
    {
        if(sumCost<minCost)minCost=sumCost;
    }
    else
    {
        for(int i=0;i<n;i++)
        {
            sumCost+=arr[k][i];
            if(vis[i]!=1&&sumCost<minCost)
            {
                vis[i]=1;
                dfs(k+1,n);
                vis[i]=0;
            }
            sumCost-=arr[k][i];
        }
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>arr[i][j];
    dfs(0,n);
    cout<<minCost<<endl;
    return 0;
}
  • 整数变换问题
#include <iostream>
using namespace std;
/**
*整数i的两种变换定义为 , (向下取整);设计一个算法求给定两个整数a和b,用最少次数的 和 变换将整数a变换为b;例如
实现提示:
观察f和g两个操作可知,f总是使得i变大,g总是使得i变小。因此在决定让x执行哪个操作之前可以先判断i和目标值m之间的大小关系。如果x>m,就让其执行g操作;
反之,执行f操作。
问题的解分为两种情况,一种是有解,即n可以通过函数变换成m;另一种是无解,即n无法通过函数变换成m。
有解的情况比较容易,只需要判断最后的i是否等于m即可。如果i等于m,那么说明n已经被变换成m了,递归返回。
*/
int fn[1001];//0=f,1=g
int arr[1001];//存放计算结果
int cnt = 0;
int test(int n)
{
    for(int i=0;i<cnt-1;i++)
        if(arr[i]==n)return 0;

    return 1;
}
void display(int n,int m)
{
    cout<<m<<"=";
    for(int i=cnt-1;i>=0;i--)
        if(fn[i]==1)cout<<"g";
        else cout<<"f";
    cout<<"("<<n<<")"<<endl;
}
void dfs(int n,int m,int num)
{
    if(n==m)
    {
        display(num,m);
        return ;
    }
    else
    {
        if(n>m){n=n/2;fn[cnt]=1;}
        else n=n*3;
        arr[cnt++]=n;

        if(test(n)&&cnt<1000)dfs(n,m,num);
        else {
            cout<<"无解"<<endl;
            return;
        }
    }
}
int main()
{
    int n,m;
    cin>>n>>m;
    dfs(n,m,n);
    return 0;
}
  • 子集和问题
#include <iostream>
using namespace std;
int arr[1001];
int dp[1001];
int sum=0;
int cnt=0;
void dfs(int n,int m,int k)
{

    if(sum>m||k>n)return;
    if(sum==m)
    {
        cnt++;
        for(int i=0;i<n;i++)
            if(dp[i])cout<<arr[i]<<" ";
        cout<<"\n";

    }
    else
        for(int i=0;i<2;i++)
            if(i==0){
                sum+=arr[k];
                dp[k]=1;
                dfs(n,m,k+1);
                dp[k]=0;
                sum-=arr[k];
            }
            else dfs(n,m,k+1);
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        cin>>arr[i];
    }
    dfs(n,m,0);
    if(cnt==0)cout<<"No Solution"<<endl;
    return 0;
}

五、动态规划

  • 最大子段和

最大子段和
给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。
要求算法的时间复杂度为O(n)。
输入格式:
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。
输出格式:
输出最大子段和。
输入样例:
在这里给出一组输入。例如:
6
-2 11 -4 13 -5 -2
输出样例:
在这里给出相应的输出。例如:
20

#include <iostream>
using namespace std;
int main()
{
    int n;
    int maxSum=0;
    int nowSum=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int num;
        cin>>num;
        nowSum+=num;
        if(nowSum>maxSum)maxSum=nowSum;
        if(nowSum<0)nowSum=0;
    }
    cout<<maxSum<<endl;
    return 0;
}
  • 0-1背包

给定n(n<=100)种物品和一个背包。物品i的重量是wi,价值为vi,背包的容量为C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。
输入格式:
共有n+1行输入: 第一行为n值和c值,表示n件物品和背包容量c; 接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。
输出格式:
输出装入背包中物品的最大总价值。
输入样例:
在这里给出一组输入。例如:
5 10
2 6
2 3
6 5
5 4
4 6
输出样例:
在这里给出相应的输出。例如:
15

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int dp[1001][1001]={0};
int volume[1001];//体积
int value[1001];//价值
int main()
{
    int n,v;
    memset(dp,0,sizeof(dp));
    cin>>n;
    cin>>v;
    for(int i=1;i<=n;i++)
    cin>>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];
            }

        }
    cout<<maxNum<<endl;
    return 0;
}
  • 回文串问题

一个字符串,如果从左到右读和从右到左读是完全一样的,比如"aba",我们称其为回文串。现在给你一个字符串,可在任意位置添加字符,求最少添加几个字符,才能使其变成一个回文串。
输入格式:
任意给定的一个字符串,其长度不超过1000.
输出格式:
能变成回文串所需添加的最少字符数。
输入样例:
在这里给出一组输入。例如:
Ab3bd
Abb
输出样例:
在这里给出相应的输出。例如:
2
1

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int dp[1001][1001];

int main()
{
    char ch1[1001];
    char ch2[1001];
    string s;
    while(cin>>s)
    {

        for(int i=0;i<s.length();i++)
        {
           ch1[i]=s[i];
           ch2[i]=s[s.length()-i-1];
        }
        int len = s.length();
        for(int i=1;i<=s.length();i++)
            for(int j=1;j<=s.length();j++)
            {
                if(ch1[i-1]==ch2[j-1])
                {
                    //记录回文串长
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    if(dp[i-1][j]>dp[i][j-1])dp[i][j]=dp[i-1][j];
                    else dp[i][j]=dp[i][j-1];
                }
            }
        cout<<(len - dp[len][len])<<endl;
    }
    return 0;
}
  • 矩阵链相乘问题
#include <iostream>
#include<stdio.h>
using namespace std;
long long dp[2001][2001];//尼玛大哥题目不是100吗????看来以后数组都要开最大了
int arr[2002][2];
int main()
{   int n;
    cin>>n;
    cin>>arr[1][0];
    for(int i=1;i<=n;i++)
    {
        if(i>1)arr[i][0]=arr[i-1][1];
        cin>>arr[i][1];
    }
    //dp代表由i乘到j个矩阵所需最小乘数
    for(int i=n;i>=1;i--)
        for(int j=i+1;j<=n;j++)  //填充dp
        {
            if(j==i+1)dp[i][j]=arr[i][0]*arr[i][1]*arr[j][1];

            for(int k=i;k<j;k++)
            {
                int num = dp[k+1][j]+dp[i][k]+arr[i][0]*arr[k][1]*arr[j][1];
                if(k==i||num<dp[i][j])dp[i][j]=num;
            }
        }
    cout<<dp[1][n]<<endl;
    return 0;
}
  • 寻宝

明有一张藏宝图,上面有m*n个房间,每个房间里面都有一个有一定价值的宝物,小明只能从左上角的房间进入收集宝物,且每次只能向右边或向下边的房间继续寻宝,最终只能从最右下的房间出来。请你帮小明计算下他最多可以收集到多少价值的宝物?
输入格式:
输入第一行给出两个正整数m,n(1=<m,n<=2000),随后给出m行数据,每行都包括n个正整数,中间用空格分割。
输出格式:
输出收集到的最大价值v,题目保证v<10^9。
输入样例:
4 4
1 18 9 3
7 10 6 12
5 13 4 15
2 11 8 16
输出样例:
78

#include <iostream>
using namespace std;
int dp[2001][2001];
int main()
{
    int m,n;
    cin>>m>>n;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        cin>>dp[i][j];
    //第一行第一列已初始化为0
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            if(dp[i-1][j]>dp[i][j-1])dp[i][j]=dp[i][j]+dp[i-1][j];
            else dp[i][j]=dp[i][j]+dp[i][j-1];
        }
    cout<<dp[m][n]<<endl;
    return 0;
}
  • “最长公共子序列” 问题的动态规划算法求解方法

最长公共子序列问题:若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int dp[100][100];
int bb[100][100];
 void LCSLength(const char *x ,const char *y,int m,int n, int c[100][100], int b[100][100])
{
       int i ,j;
       for (i = 1; i <= m; i++) c[i][0] = 0;
       for (i = 1; i <= n; i++) c[0][i] = 0;
       for (i = 1; i <= m; i++)
          for (j = 1; j <= n; j++)
          {

            if (x[i-1]==y[j-1]) {
                 c[i][j]=c[i-1][j-1]+1;
                 b[i][j]=1;
            }
            else if (c[i-1][j]>=c[i][j-1])
            {
                 c[i][j]=c[i-1][j];
                 b[i][j]=2;
            }
            else
            {    c[i][j]=c[i][j-1];
                 b[i][j]=3;
            }
         }
}
void LCS(int i ,int j, const char *x ,int b[100][100])
{
      if (i ==0 || j==0) return;
      if (b[i][j]== 1) //左上
      {
           LCS(i-1,j-1,x,b);
           printf("%c",x[i-1]);
      }
      else if (b[i][j]== 2) //上
           LCS(i-1,j,x,b);
      else LCS(i,j-1,x,b);  //左
}

int main()
{
    string str1;
    string str2;
    while(cin>>str1>>str2)
    {

        int m = str1.length();
        int n = str2.length();
        memset(dp,0,sizeof(dp));
        memset(bb,0,sizeof(bb));
        LCSLength(str1.c_str(),str2.c_str(),m,n,dp,bb);
        LCS(m,n,str1.c_str(),bb);
    }
    return 0;
}
  • 计算矩阵连乘积

在科学计算中经常要计算矩阵的乘积。矩阵A和B可乘的条件是矩阵A的列数等于矩阵B的行数。若A是一个p×q的矩阵,B是一个q×r的矩阵,则其乘积C=AB是一个p×r的矩阵。由该公式知计算C=AB总共需要pqr次的数乘。其标准计算公式为:
现在的问题是,给定n个矩阵{A1,A2,…,An}。其中Ai与Ai+1是可乘的,i=1,2,…,n-1。要求计算出这n个矩阵的连乘积A1A2…An,最少的乘法次数。
递归公式:
请编写程序实现矩阵连乘问题的动态规划算法,自己设计不少于3组的测试数据,要求显示出最少的乘法次数,以及最优计算次序。

#include <iostream>
#include<stdio.h>
using namespace std;
long long dp[2001][2001];
int arr[2002][2];
int dd[2001][2001];
void dfs(int i,int j)
{

    if(i==j)
        cout<<"A"<<i;
    else if(i==j-1)
        cout<<"(A"<<i<<"A"<<j<<")";
    else{
        cout<<"(";
        dfs(i,dd[i][j]);
        dfs(dd[i][j]+1,j);
        cout<<")";
    }
}
int main()
{   
    int n;
    cin>>n;
    cin>>arr[1][0];
    for(int i=1;i<=n;i++)
    {
        if(i>1)arr[i][0]=arr[i-1][1];
        cin>>arr[i][1];
    }
    //dp代表由i乘到j个矩阵所需最小乘数
    for(int i=n;i>=1;i--)
        for(int j=i+1;j<=n;j++)  //填充dp
        {
            if(j==i+1){
                dp[i][j]=arr[i][0]*arr[i][1]*arr[j][1];
            }
            for(int k=i;k<j;k++)
            {
                int num = dp[k+1][j]+dp[i][k]+arr[i][0]*arr[k][1]*arr[j][1];
                if(k==i||num<dp[i][j]){
                        dp[i][j]=num;
                        dd[i][j]=k;
                }
            }
        }
    dfs(1,n);
    cout<<"\n";
    cout<<dp[1][n]<<endl;
    return 0;
}
  • 最大子段和

给定由n个整数(可能有负整数)组成的序列(a1,a2,…,an),最大子段和问题要求该序列形如 的最大值(1<=i<=j<=n),当序列中所有整数均为负整数时,其最大子段和为0。
1) 用分治法求解。(选做)
2) 用动态规划法求解。

#include <iostream>
using namespace std;
int main()
{
    int n;
    int maxSum=0;
    int nowSum=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int num;
        cin>>num;
        nowSum+=num;
        if(nowSum>maxSum)maxSum=nowSum;
        if(nowSum<0)nowSum=0;

    }
    cout<<maxSum<<endl;
    return 0;
}
  • 最大k乘积问题。

问题描述:设X是一个n位十进制整数,如果将X划分为K段,则可得到K个整数,这K个整数的乘积称为X的一个K乘积。请设计算法并编程实现,对于给定的X 和K,求出X的最大K乘积。
输入:X,K,n
输出:X的最大K乘积。

#include <iostream>
#include<stdio.h>
using namespace std;

int fun(char *num,int i,int j)
{
    int sum = 0;
    while(i<=j)
    {
        sum=sum*10+(num[i]-'0');
        i++;
    }
    return sum;
}
int main()
{
    int k,n;
    cin>>n>>k;
    getchar();
    char num[n+1];
    int dp[n+1][n+1];
    for(int i=1;i<=n;i++)
        cin>>num[i];
    dp[1][1]=num[1]-'0';
    for(int i=2;i<=n;i++)
        dp[i][1]=dp[i-1][1]*10+(num[i]-'0');
    for(int i=2;i<=k;i++)
    {
        int maxNum=-1;
        for(int j=i;j<=n;j++){
            for(int g=1;g<j;g++)
            {
                int value = dp[g][i-1]*fun(num,g+1,j);
                if(value>maxNum)maxNum=value;
            }
            dp[j][i]=maxNum;
        }
    }
    cout<<"最大K乘积:"<<dp[n][k]<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值