算法设计与分析习题大全
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;
}