“王晓东” 版DP经典习题总结&AC代码(C++)

最小m段和问题

【问题描述】
给定 n 个整数组成的序列,现在要求将序列分割为 m 段,每段子序列中的数在原序列
中连续排列。如何分割才能使这 m段子序列的和的最大值达到最小?

算法设计:
给定 n 个整数组成的序列,计算该序列的最优 m段分割,使 m段子序列的和的最大值达到最小。

【输入形式】
输入数据:第 1 行中有 2 个正整数 n 和 m。正整数 n 是序列的长度;正整数 m是分割的段数。接下来的一行中有 n 个整数。
【输出形式】
将计算结果输出:第 1 行中的数是计算出的 m段子序列的和的最大值的最小值。

【样例输入】
1 1
10
【样例输出】
10

/*
--------------------------------------
--------------------------------------
------Problem: 8918.最小m段和问题----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include <bits/stdc++.h>
#define ll long long 
#define inf 0x3f3f3f3f
using namespace std;
 
int n,m,a[1005];
int dp[1005][1005];
int sum[1005][1005];
 
int main()
{
    
    
    cin >> n >> m;
    for(int i=1; i<=n; i++)    cin >> a[i];
    
    for(int i=1; i<=n; i++) 
        for(int j=1; j<=m; j++) 
            dp[i][j] = 1000000000;
        
    for(int i=1; i<=n; i++) {
    
    
        int num = 0;
        for(int j=i; j<=n; j++) {
    
    
            num += a[j];
            sum[i][j] = num;
        }
        dp[i][1] = sum[1][i];
    }
    
    for(int i=2; i<=n; i++) {
    
    
        for(int j=2; j<=i && j<=m; j++) {
    
    
            int p;
            for(int k=1; k<i; k++) 
                dp[i][j] = min(dp[i][j], max(dp[k][j - 1], sum[k + 1][i]));
        }
    }
    
    cout << dp[n][m] << endl;
    return 0;
}

最大 k 乘积问题

【问题描述】
设 I 是一个 n 位十进制整数。如果将 I 划分为 k 段,则可得到 k 个整数。这 k 个整数的
乘积称为 I 的一个 k 乘积。试设计一个算法,对于给定的 I 和 k,求出 I 的最大 k 乘积。

算法设计:
对于给定的 I 和 k,计算 I 的最大 k 乘积。

【输入形式】
输入数据:第 1 行中有 2 个正整数 n 和 k。正整数 n 是序列
的长度;正整数 k 是分割的段数。
接下来的一行中是一个 n 位十进制整数。(n<=10)
【输出形式】
将计算结果输出:第 1 行中的数是计算出的最大 k 乘积。

【样例输入】
2 1
15
【样例输出】
15

/*
--------------------------------------
--------------------------------------
-------Problem: 8917.最大k乘积问题----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/

#include <bits/stdc++.h>
using namespace std;
int dp[15][15];     //最大乘积数组
char numstr[15];
int num[15];

int getValue(int i,int j) {
    
    
    int sum = 0;
    for(int k=i; k<j; k++) {
    
    
        sum += num[k];
        sum *= 10;
    }
    return sum + num[j];
}

void dpAlgo(int l,int k) {
    
    
    for(int i=1; i<=l; i++)
        dp[i][1] = getValue(1,i);
    for(int i=0; i<=l; i++) {
    
    
        for(int j=2; j<=k; j++) {
    
    
            int temp = 0;
            for(int d=1; d<i; d++)
                temp = max(temp,dp[d][j-1] * getValue(d+1,i));
            dp[i][j] = temp;
        }
    }
}

int main()
{
    
    
    int l,k;
    cin>>l>>k>>numstr;
    for(int i=0; i<l; i++)
        num[i+1] = numstr[i] - '0';
    dpAlgo(l,k);
    cout << dp[l][k];
    return 0;
}

石子合并问题

【问题描述】
在一个圆形操场的四周摆放着 n 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将 n 堆石子合并成一堆的最小得分和最大得分。

算法设计:
对于给定 n 堆石子,计算合并成一堆的最小得分和最大得分。

【输入形式】
输入数据:第 1 行是正整数 n,1≤n≤100,表示有 n 堆石子。
第二行有 n 个数,分别表示每堆石子的个数。
【输出形式】
将计算结果输出:第 1 行中的数是最小得分;第 2 行中的数是最大得分。

【样例输入】
4
4 4 5 9
【样例输出】
43
54

/*
--------------------------------------
--------------------------------------
--------Problem: 8920.石子合并问题----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include <bits/stdc++.h>
using namespace std;
const int INF=0x7fffffff;
int n,m,ans1,ans2;
int A[205],f1[205][205],f2[205][205];
int dfs1(int L,int R){
    
                    //求出最小得分 
    if(f1[L][R])return f1[L][R];    //已保存的状态不必搜索 
    if(L==R)    return f1[L][R]=0;    //L==R时返回0 
    int res=INF;                    //初始值赋为最大值以求最小值 
    for(int k=L;k<R;k++)            //枚举K搜索 
        res=min(res,dfs1(L,k)+dfs1(k+1,R)+A[R]-A[L-1]);
    return f1[L][R]=res;            //记录状态 
}
int dfs2(int L,int R){
    
                    //求出最大得分 
    if(f2[L][R])return f2[L][R];
    if(L==R)    return f2[L][R]=0;    //若初始值为0可省略该句 
    int res=0;                        //初始值设为0 
    for(int k=L;k<R;k++)
        res=max(res,dfs2(L,k)+dfs2(k+1,R)+A[R]-A[L-1]);
    return f2[L][R]=res;
}
int main(){
    
    
    std::ios::sync_with_stdio(false);//取消cin与stdin同步,加速读入 
    cin>>n;
    for(int i=1;i<=n;i++){
    
    
        cin>>A[i];
        A[i+n]=A[i];                //因为是环所以保存为长度为2n的链以保证不会漏解 
    }
    for(int i=1;i<=2*n;i++)            //求出前缀和 
        A[i]+=A[i-1];
    dfs1(1,2*n);dfs2(1,2*n);        //搜索出1-2n的最大得分与最小得分 
    ans1=INF;    ans2=0;
    for(int i=1;i<=n;i++){
    
    
        ans1=min(f1[i][n+i-1],ans1);//选出答案 
        ans2=max(f2[i][n+i-1],ans2);
    }
    cout<<ans1<<"\n"<<ans2;
    return 0;
}

最大长方体问题

【问题描述】
一个长,宽,高分别为 m,n,p 的长方体被分割成个 mnp 个小立方体。每个小立方体内有一个整数。试设计一个算法,计算出所给长方体的最大子长方体。子长方体的大小由它所含所有整数之和确定。

算法设计:
对于给定的长,宽,高分别为 m,n,p 的长方体,计算最大子长方体的大小。

【输入形式】
输入数据:第 1 行是 3 个正整数 m,n,p,1≤m,n,p≤50。接下来 m*n 行每行 p 个正整数,表示小立方体中的数。
【输出形式】
将计算结果输出:第 1 行中的数是计算出的最大子长方体的大小。

【样例输入】
3 3 3
0 -1 2
1 2 2
1 1 -2
-2 -1 -1
-3 3 -2
-2 -3 1
-2 3 3
0 1 3
2 1 -3
【样例输出】
14

/*
--------------------------------------
--------------------------------------
-------Problem: 8916.最大长方体问题---
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/

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

int maxsum(int n,int *a)
{
    
    
    int sum=0,b=0;
    for (int i=1; i<=n; i++) 
	{
    
    
        if(b>0)    b+=a[i];
        else    b=a[i];
        if(b>sum)    sum=b;//记录最大值
    }
    return sum;
}

int maxsum2(int m,int n, int **a){
    
    
    int sum=0;
    int *b=new int [n+1];
    for (int i=1; i<=m; i++) {
    
    
        for(int k=1; k<=n; k++)
            b[k]=0;//置0
        for(int j=i; j<=m; j++) {
    
    //动规思想,将m分成1~i和i+1~m两段
            for (int k=1; k<=n; k++)
                b[k] += a[j][k];
            int max=maxsum(n,b);
            if(max>sum)    sum=max;
        }
    }
    return sum;
}

int maxsum3(int m,int n,int p,int ***a)
{
    
    
    int sum=0;
    int **c=new int*[n+1];
    for(int i=1; i<=n; i++)    c[i]=new int [p+1];
    
    for(int i=1; i<=m; i++) 
	{
    
    
        for(int j=1; j<=n; j++) 
            for(int k=1; k<=p ; k++)    c[j][k]=0;//置0
            
        for (int l=i; l<=m; l++) {
    
    //和二维的思想一样
            for(int j=1; j<=n; j++) 
                for(int k=1; k<=p ; k++)    c[j][k]+=a[l][j][k];
            int max=maxsum2(n,p,c);
            if(max>sum)    sum=max;
        }
    }
    return sum;
}

int main()
{
    
    
    int m,n,p,***a;
    cin>>m>>n>>p;
    a=new int**[m+1];
    
    for(int i=1; i<=m; i++) a[i]=new int*[n+1]; //一维
    
    for(int i=1; i<=m; i++) 
        for (int j=1; j<=n; j++) 
            a[i][j]=new int[p+1]; //二维

    for(int i=1; i<=m; i++) 
        for(int j=1; j<=n; j++) 
            for(int k=1; k<=p; k++) 
                cin>>a[i][j][k]; //三维
    
    cout<<maxsum3(m,n,p,a);
}

序关系计数问题

【问题描述】
用关系“<”和“=”将 3 个数 A、B 和 C 依序排列时有 13 种不同的序关系:A=B=C,A=B<C,A<B=C,A<B<C,A<C<B,A=C<B,B<A=C,B<A<C,B<C<A,B=C<A,C<A=B,C<A<B,C<B<A。将n 个数(1 ≤ n ≤50)依序排列时有多少种序关系。

算法设计:
计算出将n 个数(1 ≤ n ≤50)依序排列时有多少种序关系。

【输入形式】
输入数据:输入数据有多行,每行提供一个数n 。
【输出形式】
将找到的序关系数输出:一行一个。

【样例输入】
3
5
【样例输出】
13
541

/*
--------------------------------------
--------------------------------------
-------Problem: 8923.序关系计数问题---
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include <bits/stdc++.h>
#define MAX_N 51
using namespace std;
 
__int64 dp[MAX_N][MAX_N];	//dp[i][j]:i个数,有j个‘<’链接,共有多少种情况
 
int main()
{
    
    
	int n,i,j;
	for(i=0; i<MAX_N; i++)	dp[i][0]=1;
	
	for(i=0; i<MAX_N; i++)
		for(j=1; j<i; j++)
			dp[i][j] = (j+1)*(dp[i-1][j]+dp[i-1][j-1]);
		
	
	for(i=0; i<MAX_N; i++)
		for(j=0; j<i; j++)
			dp[i][i] += dp[i][j];	//dp[i][i]存放每一行的和
		
	while(scanf("%d",&n)==1){
    
    
		printf("%I64d\n",dp[n][n]);
	}
	return 0;
}

汽车加油行驶问题

【问题描述】
给定一个N*N 的方形网格,设其左上角为起点,坐标为(1,1),X 轴向右为正,Y 轴向下为正,每个方格边长为1。一辆汽车从起点出发驶向右下角终点,其坐标为(N,N)。在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守
如下规则:(1)汽车只能沿网格边行驶,装满油后能行驶K 条网格边。出发时汽车已装满油,在起点与终点处不设油库。(2)当汽车行驶经过一条网格边时,若其 X 坐标或 Y 坐标减小,则应付费用B,否则免付费用。(3)汽车在行驶过程中遇油库则应加满油并付加油费用A。(4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)。(5)(1)~(4)中的各数N、K、A、B、C均为正整数。

算法设计:
求汽车从起点出发到达终点的一条所付费用最少的行驶路线。

【输入形式】
输入数据:
第一行是N,K,A,B,C的值,2 ≤ N ≤ 100,2 ≤ K ≤ 10。第二行起是一个N*N 的0-1方阵,每行N 个值,至N+1行结束。方阵的第i行第j 列处的值为1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻的2 个数以空格分隔。
【输出形式】
将找到的最优行驶路线所需的费用,即最小费用输出:第1行中的数是最小费用值。

【样例输入】
9 3 2 3 6
0 0 0 0 1 0 0 0 0
0 0 0 1 0 1 1 0 0
1 0 1 0 0 0 0 1 0
0 0 0 0 0 1 0 0 1
1 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 1 0
0 1 0 0 0 0 0 0 0

【样例输出】
12

/*
--------------------------------------
--------------------------------------
-----Problem: 8910.汽车加油行驶问题---
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include<bits/stdc++.h>
#include<set>
#include<map>
using namespace std;
#define MAX 120

inline int read()
{
    
    
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line
{
    
    
    int v,next,w;
}e[MAX*MAX*MAX];

int h[MAX*MAX],cnt=1,tot;
int m[MAX*MAX],g[MAX][MAX],n,K,A,B,C;
bool vis[MAX*MAX][15];

inline void Add(int u,int v,int w)
{
    
    
    e[cnt]=(Line){
    
    v,h[u],w};h[u]=cnt++;
}

int dis[MAX*MAX][15];

void SPFA()
{
    
    
    memset(dis,63,sizeof(dis));
    dis[g[1][1]][K]=0;
    queue<int> Q,Q1;
    Q.push(g[1][1]);Q1.push(K);
    while(!Q.empty())
    {
    
    
        int u=Q.front(),t=Q1.front();
        Q.pop();Q1.pop();
        if(t!=0)
        {
    
    
            for(int i=h[u];i;i=e[i].next)
            {
    
    
                int v=e[i].v,gg=t-1,Dis=dis[u][t]+e[i].w;
                if(m[v])gg=K,Dis+=A;
                if(dis[v][gg]>Dis)
                {
    
    
                    dis[v][gg]=Dis;
                    if(!vis[v][gg])vis[v][gg]=true,Q.push(v),Q1.push(gg);
                }
            }
        }
        int v=u,gg=K,Dis=dis[u][t]+C+A;
        if(dis[v][gg]>Dis)
        {
    
    
            dis[v][gg]=Dis;
            if(!vis[v][gg])vis[v][gg]=true,Q.push(v),Q1.push(gg);
        }
        vis[u][t]=false;
    }
}

int main()
{
    
    
    n=read();K=read();A=read();B=read();C=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            g[i][j]=++tot,m[g[i][j]]=read();;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
    
    
            if(i!=n)  Add(g[i][j],g[i+1][j],0);
            if(j!=n)  Add(g[i][j],g[i][j+1],0);
            if(i!=1)  Add(g[i][j],g[i-1][j],B);
            if(j!=1)  Add(g[i][j],g[i][j-1],B);
        }
    SPFA();
    int ans=1e9;
    for(int i=0;i<=K;++i)ans=min(ans,dis[g[n][n]][i]);
    printf("%d\n",ans);
    return 0;
}

最少硬币问题

【问题描述】
设有 n 种不同面值的硬币,各硬币的面值存于数组 T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组 Coins[1:n]中。对任意钱数 0≤m≤20001,设计一个用最少硬币找钱 m 的方法。

算法设计:
对于给定的 1≤n≤10,硬币面值数组 T 和可以使用的各种面值的硬币个数数组 Coins,以及钱数 m,0≤m≤20001,计算找钱 m的最少硬币数。

【输入形式】
输入数据:第一行中只有 1 个整数给出n 的值,第 2 行起每
行 2 个数,分别是 T[j]和 Coins[j]。最后 1 行是要找的钱数 m。
【输出形式】
将计算出的最少硬币数输出。问题无解时输出-1。

【样例输入】
3
1 3
2 3
5 3
18
【样例输出】
5

/*
--------------------------------------
--------------------------------------
--------Problem: 8913.最少硬币问题----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/

/*多重背包(硬币种类有限制,硬币数目有限制)*/
#include <iostream>
using namespace std;

const int maxvalue = 20001;
const int coinnum = 15;

int mymin(int a,int b){
    
    
    return a < b ? a : b;
}

int main()
{
    
    
    int n;
    cin >> n;
    int coin[coinnum];
    int coincount[coinnum];
    for(int i=0; i<n; i++)
    {
    
    
        cin >> coin[i];
        cin >> coincount[i];
    }
    int m;
    cin >> m;
    //钱数为dp[i]是的硬币数目
    int *dp = new int[maxvalue]();
    //重点错误
    //for(int i=0; i<=m; i++)是错的
    for(int i=1; i<=m; i++)  dp[i] = maxvalue;
    int i,j,k;
    for(i=0; i<n; i++)//硬币面值的种数
    {
    
    
        for(j=1; j<=coincount[i]; j++)//硬币的面值的个数
        {
    
    
            for(k=m; k>=coin[i]; k--) //动态迁移方程为
                dp[k] = mymin(dp[k - coin[i]] + 1, dp[k]);
        }
    }
    
    if(dp[m] == maxvalue)
        cout << -1 << endl;
    else cout << dp[m] << endl;
    
    return 0;
}

租用游艇问题

【问题描述】
长江游艇俱乐部在长江上设置了 n 个游艇出租站 1,2,…,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i 到游艇出租站 j 之间的租金为 r(i,j),1≤i<j≤n。试设计一个算法,计算出从游艇出租站 1 到游艇出租站 n 所需的最少租金。

算法设计:
对于给定的游艇出租站 i 到游艇出租站 j 之间的租金为 r(i,j),1≤i<j≤n,计算从游艇出租站 1 到游艇出租站 n 所需的最少租金。

【输入形式】
输入数据:第 1 行中有 1 个正整数 n(n<=200),表示有 n个游艇出租站。接下来的 n-1 行是 r(i,j),1≤i<j≤n。
【输出形式】
将计算出的从游艇出租站 1 到游艇出租站 n 所需的最少租金输出。

【样例输入】
3
5 15
7
【样例输出】
12

/*
--------------------------------------
--------------------------------------
--------Problem: 8924.租用游艇问题----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/

#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

int n,v[222][222];
int dp[222];//dp[i]表示到第i个游艇出租站所需的最少租金

int main()
{
    
    
    scanf("%d",&n);
    for(int i=1; i<n; i++)
      for(int j=i+1; j<=n; j++)  
		scanf("%d",&v[i][j]);
		
    memset(dp,INF,sizeof(dp));
    
    dp[1]=0;
    for(int i=2; i<=n; i++)
      for(int j=1; j<i; j++)
        dp[i] = min(dp[i],dp[j]+v[j][i]);
        
    printf("%d\n",dp[n]);
    return 0;
}

红黑树的红色内结点问题

【问题描述】
红黑树是一类特殊的二叉搜索树,其中每个结点被“染成”红色或黑色。若将二叉搜索树结点中的空指针看作是指向一个空结点,则称这类空结点为二叉搜索树的前端结点。并规定所有前端结点的高度为-1。

一棵红黑树是满足下面“红黑性质”染色二叉搜索树:
(1)每个结点被染成红色或黑色;
(2)每个前端结点为黑色结点;
(3)任一红结点的儿子结点均为黑结点;
(4)在从任一结点到其子孙前端结点的所有路径上具有相同的黑结点数。

从红黑树中任一结点 x 出发(不包括结点 x),到达一个前端结点的任意一条路径上的黑结点个数称为结点 x 的黑高度,记作 bh(x)。红黑树的黑高度定义为其根结点的黑高度。图示的二叉搜索树是一棵红黑树。标在结点旁边的数字是相应结点的黑高度。
在这里插入图片描述
算法设计:
给定正整数 n,试设计一个算法,计算出在所有含有 n 个结点的红黑树中,红色内结点个数的最小值和最大值。

【输入形式】
输入数据:第一行是正整数 n,1<n<5000。
【输出形式】
将红色内结点个数的最小值和最大值输出:第 1 行是最小值,第 2行是最大值。

【样例输入】
8
【样例输出】
1
4

/*
--------------------------------------
--------------------------------------
------8830. 红黑树的红色内结点问题----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include <bits/stdc++.h>
using namespace std;
int n,m,ans;
 
int main()
{
    
    
	scanf("%d",&n);
	m=n+1;
	while(m>1)	{
    
    
		ans+=m&1;m>>=1;
	}
	printf("%d\n",ans);
	
	m=n+1;ans=0;
	while(m>1)
	{
    
    
		if(m==2)  ans++;
		if((m&3)==1)  ans+=m/4*2-1,m/=4,m++;
		else if((m&3)==2)  ans+=m/4*2,m/=4,m++;
		else if((m&3)==3)  ans+=m/4*2+1,m/=4,m++;
		else  ans+=m/4*2,m/=4;
	}
	printf("%d\n",ans);
	return 0;
}

编辑距离问题

【问题描述】
设 A 和 B 是 2 个字符串。要用最少的字符操作将字符串 A 转换为字符串 B。这里所说的字符操作包括
(1)删除一个字符;
(2)插入一个字符;
(3)将一个字符改为另一个字符。

将字符串 A 变换为字符串 B 所用的最少字符操作数称为字符串 A 到 B 的编辑距离,记为d(A,B)。试设计一个有效算法,对任给的2个字符串A和B,计算出它们的编辑距离d(A,B)。

算法设计:
对于给定的字符串 A 和字符串 B,计算其编辑距离 d(A,B)。

【输入形式】
输入数据:第一行是字符串 A,文件的第二行是字符串 B。
【输出形式】
将编辑距离 d(A,B)输出到第 1 行中。

【样例输入】
fxpimu
xwrs
【样例输出】
5

/*
--------------------------------------
--------------------------------------
---------------8829. 编辑距离问题-----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include <bits/stdc++.h>
using namespace std;
int d[2005][2005];

/*
输入的两个字符数组为a[], b[],从下标为0开始初始化
长度分别为length_a, length_b 
数组d[m][n]存放从a[1:m] 变为 b[1:n]所需要的最少操作
递归公式:
	d[i][j] = 0,    i=0或j=0 时(即数组的第一行和第一列均为0)
	1<=i<=length_a, 1<=j<=length_b 
	d[i][j] = d[i-1][j-1],  a[i-1] == b[j-1]  
	d[i][j] = min(d[i-1][j-1]+1, d[i][j-1]+1, d[i-1][j]+1),   a[i-1] != b[j-1]
最优值:d[length_a][length_b]
*/

int min(int a, int b, int c)
{
    
    
	int temp = a;
	if(temp>b)	temp = b;
	if(temp>c)	temp =c;
	return temp;
}
 
int edit(char *a, char *b)
{
    
    
	int length_a = strlen(a);
	int length_b = strlen(b);
	
	for(int i=0; i<=length_a; i++)	d[i][0] = i;
	for(int i=0; i<=length_b; i++)	d[0][i] = i;
	
	for(int i=1; i<=length_a; i++) {
    
    
		for(int j=1; j<=length_b; j++) {
    
    
			if(a[i-1] == b[j-1])
				d[i][j] = min(d[i-1][j-1], d[i][j-1]+1, d[i-1][j]+1);
			else
				d[i][j] = min(d[i-1][j-1]+1, d[i][j-1]+1, d[i-1][j]+1);
		}
	}
	return d[length_a][length_b];
}
 
int main()
{
    
    
	char a[2000], b[2000];
	cin>>a>>b;
	cout<<edit(a,b);
} 

圈乘运算问题

【问题描述】
关于整数的 2 元圈乘运算⊗定义为(X⊗Y)=10 进制整数 X 的各位数字之和 * 10 进制整数 Y 的最大数字+Y 的最小数字。例如,(9⊗30)=9*3+0=27。对于给定的 10 进制整数 X 和 K,由 X 和⊗运算可以组成各种不同的表达式。试设计一个算法,计算出由 X 和⊗运算组成的值为 K 的表达式最少需用多少个⊗运算。

算法设计:
给定 10 进制整数 X 和 K (1≤X,K≤1020) 。计算由 X 和⊗运算组成的值为 K 的表达式最少需用多少个⊗运算。

【输入形式】
输入数据:每一行有 2 个 10 进制整数 X 和 K。最后一行是 0 0(以0 0结束)。
【输出形式】
将找到的最少⊗运算个数输出。

【样例输入】
3 12
0 0
【样例输出】
1

这题TLE了,明天蓝桥,之后AC了再补上

/*
--------------------------------------
--------------------------------------
---------------8911. 圈乘运算问题-----
--------------------------------------
----------------------Author----------
--------------------------------------
----------------------XZITXX----------
--------------------------------------
*/
#include <bits/stdc++.h>
using namespace std;
const int MAX=81*30+9,MIN=-1;

int NumLen(int x){
    
    //统计位数
	int len = 0;
	while (x>0){
    
    
		len++;
		x /= 10;
	}
	return len;
}
 
void Init(int* a, int x){
    
    //作初始化
	int min = 10;
	int max = 0;
	int num = 0;
	int tend = 0;
	while (x > 0){
    
    
		tend = x % 10;
		x /= 10;
		num += tend;
		if (tend > max){
    
    
			max = tend;
		}
		if (tend < min){
    
    
			min = tend;
		}
	}
	a[0] = MAX;//存放最小圈乘次数
	a[1] = num;//存放各位数之和
	a[2] = min;//存放最小的数
	a[3] = max;//存放最大的数
}
 
int main()
{
    
    
	int x,k;
	while(~scanf("%d%d",&x,&k)&&x!=0&&k!=0)
	{
    
    
		int n = NumLen(x);//表示x的位数
	    int m = 81*n+9;//b最小可以2为数组合,最大为9,最小为9.所以(x的各位数相加)*9+9;x的各位数相加最大为9*n;
	    if(m<177){
    
    //因为y可能是2位数,导致1位数的x圈乘后的结果为2位数
		    m = 177;
	    }
	    if (k>m){
    
    //若k大于x的最大圈乘数则退出
		    return -1;
	    }
	    int** r = new int*[m+2];//建立二维数组
	    for (int i=0; i<m+1; i++){
    
    //遍历每
		    r[i] = new int[4];
		    Init(r[i],i);
	    }
	    r[m+1] = new int[4];
    	Init(r[m+1], x);//将起始x写到数组的最后
	    r[m+1][0] = 0;//变成自己不需要圈乘
	    //开始操作
	    int flag = 1;
	    while (flag){
    
    //不断在序列中更新
		    flag = 0;
		    for (int i=1; i<=m+1; i++){
    
    //寻找X
			    if (r[i][0] < MAX){
    
    
				    for (int j=1; j<=m+1; j++){
    
    //寻找Y
					    if (r[j][0] < MAX){
    
    
						    int tend = r[i][1] * r[j][3] + r[j][2];//计算圈乘结果
						    if (r[tend][0]>r[i][0]+r[j][0]+1){
    
    //与原来的圈乘生产该数的次数对比找最小
							    flag = 1;//若有变化则更新x的寻值
							    r[tend][0] = r[i][0] + r[j][0] + 1; //r[i][0]为得到x的圈乘次数,r[j][0]位得到y的圈乘次数
						    }//endif
					    }//endif
				    }
			    }//endif
		    }
	    }
	    cout << r[k][0] << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Luoxiaobaia/article/details/109079887