2024年ZZU仿真实验室第一次机试

(采用c++代码,其实就是输入cin>>和输出cout<<和C语言不同,并未用到STL)

7-1 判断上三角矩阵

上三角矩阵指主对角线以下的元素都为0的矩阵;主对角线为从矩阵的左上角至右下角的连线。

本题要求编写程序,判断一个给定的方阵是否上三角矩阵。

输入格式:

输入第一行给出一个正整数T,为待测矩阵的个数。接下来给出T个矩阵的信息:每个矩阵信息的第一行给出一个不超过10的正整数n。随后n行,每行给出n个整数,其间以空格分隔。

输出格式:

每个矩阵的判断结果占一行。如果输入的矩阵是上三角矩阵,输出“YES”,否则输出“NO”。

输入样例: 

3
3
1 2 3
0 4 5
0 0 6
2
1 0
-8 2
3
1 2 3
1 4 5
0 -1 6

输出样例

YES
NO
NO

这个题目只需遍历矩阵主对角线以下的位置是否为出现了零,用一个flag1记录是否出现零,最后输出即可。需要把握每行遍历多少就行了。

#include<bits/stdc++.h>
using namespace std;
int a[20][20];
int main (){
    int t,n;
    cin>>t;
    for(int z=0;z<t;z++){
        cin>>n;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) cin>>a[i][j];
        int flag1=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i-1;j++) if(a[i][j]) flag1=0;
        if(flag1) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

 7-2输出三角形字符阵列

本题要求编写程序,输出n行由大写字母A开始构成的三角形字符阵列。

输入格式:
输入在一行中给出一个正整数n(1≤n<7)。

输出格式:
输出n行由大写字母A开始构成的三角形字符阵列。格式见输出样例,其中每个字母后面都有一个空格。

输入样例:
4
输出样例:
A B C D 
E F G 
H I 
J

 这题也只需用一个数从A的ascll码65开始一直加就行了,输出的时候转换为char类型。把握好每行输出的个数递减,每次输出后这个数加一。

#include<bits/stdc++.h>
using namespace std;
int main (){
    int n,zxx=65;
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n-i+1;j++){
            cout<<(char)zxx<<" ";
            zxx++;
        }
        cout<<endl;
    }
    return 0;
}

7-3统计素数并求和 

本题要求统计给定整数M和N区间内素数的个数并对它们求和。

输入格式:

输入在一行中给出两个正整数M和N(1≤M≤N≤500)。

输出格式:

在一行中顺序输出M和N区间内素数的个数以及它们的和,数字间以空格分隔。

输入样例:

10 31

输出样例:

7 143

 这题就是普通的判断素数题,遍历区间,如果是素数个数加一,并且和再加上相应的值。

#include<bits/stdc++.h>
using namespace std;
int isprime(int num){
    if(num<=1) return 0;
    for(int i=2;i<=sqrt(num);i++){
        if(num%i==0) return 0;
    }
    return 1;
}
int main (){
    int m,n,zzxx=0,zzx=0;
    cin>>m>>n; 
    for(int i=m;i<=n;i++){
        if(isprime(i)){
            zzxx++;
            zzx+=i;
         }
    }
    cout<<zzxx<<" "<<zzx;
    return 0;
}

7-4 求出1到n之间所有奇数的阶乘值

编写程序,输出1到n之间所有奇数的阶乘值。

输入格式:

从键盘输入正整数n的值(不超过12)。

输出格式:

按输出样例的形式输出计算的结果。

输入样例1:

10

输出样例1:

1! = 1
3! = 6
5! = 120
7! = 5040
9! = 362880

输入样例2:

1

输出样例2:

1! = 1

 这道题直接遍历,然后如果是奇数,直接用循环算出其阶乘即可,有一点需要注意每次输出一个阶乘如果从一开始乘的话比较浪费时间,可以用一个值存住上一个输出的值,下一次只需要从上一个值乘到下一个就可以了,因为每次输出之间的结果相互之间有关系,这题数据量不大也可以不用这种方法。同时一个数与1进行合取运算如果它是奇数,那么返回值为1,如果是偶数返回值为0,这样比直接判断余2是否等于零快。

#include<bits/stdc++.h>
using namespace std;
int main (){
    int n,zzxx=1,last=1;
    cin>>n;
    for(int i=1;i<=n;i++){
        if(i&1){
            for(int j=last+1;j<=i;j++) zzxx*=j;
            last=i;
            cout<<i<<"! = "<<zzxx<<endl;
        }
    }
    return 0;
}

7-5 整数分解为若干项之和

将一个正整数N分解成几个正整数相加,可以有多种分解方法,例如7=6+1,7=5+2,7=5+1+1,…。编程求出正整数N的所有整数分解式子。

输入格式:

每个输入包含一个测试用例,即正整数N (0<N≤30)。

输出格式:

按递增顺序输出N的所有整数分解式子。递增顺序是指:对于两个分解序列N1​={n1​,n2​,⋯}和N2​={m1​,m2​,⋯},若存在i使得n1​=m1​,⋯,ni​=mi​,但是ni+1​<mi+1​,则N1​序列必定在N2​序列之前输出。每个式子由小到大相加,式子间用分号隔开,且每输出4个式子后换行。

输入样例:

7

输出样例:

7=1+1+1+1+1+1+1;7=1+1+1+1+1+2;7=1+1+1+1+3;7=1+1+1+2+2
7=1+1+1+4;7=1+1+2+3;7=1+1+5;7=1+2+2+2
7=1+2+4;7=1+3+3;7=1+6;7=2+2+3
7=2+5;7=3+4;7=7

这道题就是DFS,深度优先遍历。dfs一般都需要使用到递归。 根据分支一直找,直到找到头,然后再复原。本题就是一直找相应的数,然后把这个数加到sum上,每加一个数判断sum的值是否等于n,如果小于n继续找,如果等于n,输出然后再返回,如果大于n,也会返回。避免爆栈。返回时要把sum的值归为加上这个数之前的值,因为还要尝试加上别的值。这个复原我们使用递归,递归就会帮我们做,但有时候如果引入别的量就需要我们自己去做。这题更形象点的理解就是把每一位填上1到n都试一遍,如果不够下一位再尝试1到n,如果正好等于直接输出,大于了就返回。但因为是深度优先,所以会把第一位填1的情况都试完,然后再试第一位填2的情况。依此类推。

#include<bits/stdc++.h>
using namespace std;
int path[35],n,zzxx;
void dfs(int begin,int num,int sum){
    if(sum==n){
        if(zzxx!=0) cout<<";";
        cout<<n<<"="<<path[1];
        for(int i=2;i<=num;i++) cout<<"+"<<path[i];
        zzxx++;
        if(zzxx%4==0) {
            cout<<endl;
            zzxx=0;
        }      
    }
    if(sum>n) return ;
    for(int i=begin;i<=n;i++) {
        path[num+1]=i;
        dfs(i,num+1,sum+i);
    }
}
int main (){
    cin>>n;
    dfs(1,0,0);
    return 0;
}

7-6 队列操作

请实现一个MyQueue类,实现出队,入队,求队列长度.

实现入队函数 void push(int x);
实现出队函数 int pop();
实现求队列长度函数 int size();

输入格式:

每个输入包含1个测试用例。每个测试用例第一行给出一个正整数 n (n <= 10^6) ,接下去n行每行一个数字,表示一种操作:
1 x : 表示从队尾插入x,0<=x<=2^31-1。
2 : 表示队首元素出队。
3 : 表示求队列长度。

输出格式:

对于操作2,若队列为空,则输出 “Invalid”,否则请输出队首元素。 对于操作3,请输出队列长度。
每个输出项最后换行。

输入样例:

5
3
2
1 100
3
2

输出样例:

0
Invalid
1
100

这道题就是普通的题,前提得了解队列的特性,先进先出。根据一个front指向队首,rear指向队尾,然后出队时判断是否队空 就行了,队空条件即为front==rear。(这道题不会出现队满)这时候队满和队空就会矛盾,通常采用循环队列来增加空间的利用率。可以采用多种方法来避免出现这种情况。(感兴趣可以搜)

#include<bits/stdc++.h>
using namespace std;
#define SZIE 1000006
int q[1000006],n;
int front,rear;
int isempty(){
    if(front==rear) return 1;
    else return 0;
}
int size(){
    return rear-front;
}
void push(int num){
    q[rear++]=num;
}
void pop(){
    if(isempty()) cout<<"Invalid"<<endl;
    else cout<<q[front++]<<endl;
}
int main (){
    cin>>n;
    for(int i=0;i<n;i++){
        int zzxx;
        cin>>zzxx;
        switch(zzxx){
            case 1:{
                int zzx;
                cin>>zzx;
                push(zzx);
                break;
            }
            case 2:{
                pop();
                break;
            }
            case 3:{
                cout<<size()<<endl;
                break;
            }
        }
    }
    return 0;
}

7-7 DFS

在数独游戏中,目标是将1到9(含)之间的整数放入一个9x9网格,这样每行、每列和九个3x3框中的每一个都包含所有九个数字。起始板部分填充,以便逻辑推断其他单元格的值。数独游戏的难度各不相同,解决最难的游戏需要复杂的分析方法。然而,在这个问题中,您将实现最简单的方法之一,交叉阴影。

在交叉图案填充中,我们从九个数字中选择一个,并为网格中的每个数字引用,划掉相应的行、列和3x3框。现在,寻找任何3x3的盒子,其中只有一个可能的数字位置,并将其放置在那里。

下面的第一张图片显示了一个非常稀疏的数独网格。然而,即使在这个网格中,也可以使用交叉阴影推断左上角单元格中的数字是4,如第二幅图所示。

121.png

你将得到一个部分填充的网格。您的任务是对不同的数字重复应用交叉阴影法,直到不能再对任何数字进行扣除。

数字在网格中的初始位置可能无效。也有可能3x3盒中的数字没有可用的单元格。在这两种情况下,您都需要报告错误

输入格式:

输入将由9行组成,每行正好包含9个字符。每个字符要么是1到9之间的数字,要么是表示空单元格的句点('.')。

输出格式:

如果输入有效且求解时没有矛盾,则应以给定的格式输出网格,如果可以使用交叉阴影推断其值,则应填充单元格。否则,输出“错误”(为清楚起见,请加引号)。

输入样例1:

..9......
.....4...
.......4.
.........
.4.......
.........
.........
.........
.........

输出样例1:

4.9......
.....4...
.......4.
.........
.4.......
.........
.........
.........
.........

输入样例2:

...1...6.
18...9...
..7.642..
2.9..6.5.
.43...72.
.6.3..9.1
..265.1..
...2...97
.5...3...

输出样例2:

524137869
186529473
397864215
219476358
843915726
765382941
972658134
638241597
451793682

输入样例3:

1........
..1......
.......1.
.........
.........
.........
.........
.........
.........

输出样例3:

ERROR

这道题说实话有点难,这题首先可以通过一个结构体二维数组来储存场上地图的信息,有一个格子上填的数字,一个格子是否被遍历过,以及一个格子可能填写的值。关于这个格子被遍历指的是如果这个格子有数字我们可以排除与这个数同行同列和同九宫格的这个数的可能性,同时排除这个格子所有数字的可能性,同时标记这个格子被遍历过。首先遍历已经有数字的格子然后把与它对应的同行,同列,同九宫格的这个数字的可能性都排除,然后再遍历每个九宫格,如果有一个数字只可能出现在某一个位置,则将他填在这个位置,然后继续遍历有数字的格子,去除掉重复上一步操作,去除一些数字的可能性。如果某次没有新的数字填入格子,则说明目前的逻辑只能推出到这里。此时跳出循环。

然后再进行判断合法。

  1. 没填充的格子没有任何能填充的数字

  2. 填充了的格子行列有重复

  3. 填充了的九宫格有数不可能出现(思路来自于洛谷一题解)

    #include<bits/stdc++.h>
    using namespace std;
    struct node
    {
    	int value;
    	int checked;
    	int maybe[10];
    }a[10][10]; 
    int b[10][10]; //用于存储一个坐标在第几个九宫格上面 
    void init() 
    {
    	int ti=1;
    	for(int i=1;i<=9;i+=3)//初始化b[10][10] 
    	{
    		for(int j=1;j<=9;j+=3)
    		{
    			for(int k1=0;k1<3;k1++)
    			{
    				for(int k2=0;k2<3;k2++) 
    				{
    					b[i+k1][j+k2]=ti;
    				}
    			}
    			ti++;
    		}
    	}
    }
    bool check()//判断是否输出错误 
    {
    	for(int i=1;i<=9;i++)
    	{
    		for(int j=1;j<=9;j++)  //没填充的格子没有任何能填充的数字
    		{
    			if(a[i][j].value>0)
    				continue;
    			int k=0;
    			for(int x=1;x<=9;x++)
    				k+=a[i][j].maybe[x];
    			if(k==9)
    				return 1; 
    		}
    	}
    	for(int i=1;i<=9;i++)//判断已填充的行列是否有重复的数字 
    	{
    		for(int j=1;j<=9;j++)
    		{
    			if(a[i][j].value<1)
    				continue;
    			for(int k=1;k<=9;k++)
    			{
    				if(k==j) continue;
    				if(a[i][j].value==a[i][k].value)   return 1;
    			}
                for(int k=1;k<=9;k++){
                    if(k==i) continue;
                    if(a[i][j].value==a[k][j].value) return 1;
                }
    		}
    	}
    	for(int k=1;k<=9;k++)//填充了的九宫格是否有数不可能出现
    	{
    		for(int i=1;i<=9;i+=3)
    		{
    			for(int j=1;j<=9;j+=3)
    			{
    				int num=0;
    				for(int k1=0;k1<3;k1++)
    					for(int k2=0;k2<3;k2++)
    						num+=a[i+k1][j+k2].maybe[k];
    				if(num==9)
    				{
    					bool f=0;
    					for(int k1=0;k1<3;k1++)
    						for(int k2=0;k2<3;k2++)
    							if(a[i+k1][j+k2].value==k)
    							{
    								f=1;
    								break;
    							}
    					if(!f)
    						return 1;
    				}
    			}
    		}
    	}
    	return 0;
    }
    int main()
    {
    	init();
    	string s;
    	for(int i=1;i<=9;i++)
    	{
    		cin>>s;
    		for(int j=0;j<s.size();j++)
    			a[i][j+1].value=s[j]-'0',a[i][j].checked=0;
    	}
    
    	bool flag=1;
    	while(flag)
    	{
    		flag=0;
    		for(int i=1;i<=9;i++) 
    		{
    			for(int j=1;j<=9;j++)
    			{
    				if(a[i][j].value>0&&!a[i][j].checked)
    				{
    					for(int k=1;k<=9;k++)
    						a[i][j].maybe[k]=1;
    					for(int k=1;k<=9;k++)
    						a[i][k].maybe[a[i][j].value]=1;
    					for(int k=1;k<=9;k++)
    						a[k][j].maybe[a[i][j].value]=1; 
    					for(int k1=1;k1<=9;k1++)
    					{
    						for(int k2=1;k2<=9;k2++)
    						{
    							if(b[k1][k2]==b[i][j])
    								a[k1][k2].maybe[a[i][j].value]=1;
    						}
    					}
    					flag=1;
    					a[i][j].checked=1;
    				}
    			}
    		}
    		
    		for(int k=1;k<=9;k++) //填格 
    		{
    			for(int i=1;i<=9;i+=3)
    			{
    				for(int j=1;j<=9;j+=3)
    				{
    					int num=0,x,y;
    					for(int k1=0;k1<3;k1++)
    					{
    						for(int k2=0;k2<3;k2++)
    						{
    							if(a[i+k1][j+k2].maybe[k]==0)
    							{
    								num++;
    								x=i+k1;
    								y=j+k2;	
    							} 
    						}
    					}
    					if(num==1)
    						a[x][y].value=k;
    				}
    			}
    		} 
    	}
    	if(check())
    	{
    		cout<<"ERROR";
    		return 0; 
    	}
    	for(int i=1;i<=9;i++)
    	{
    		for(int j=1;j<=9;j++)
    		{
    			if(a[i][j].value>0)
    				cout<<a[i][j].value;
    			else
    				cout<<'.';
    		}
    		cout<<endl;
    	}
    	return 0; 
    }

 7-8 好玩的游戏:消消乐

 消消乐是一个非常流行的手机游戏。现在游戏创意设计师Jerry突发奇想设计一个如下图所示的一维消消乐游戏,Jerry想知道游戏生成的小球布局在玩家玩的过程中最高总分能得多少,现在Jerry向资深的程序员你求助,希望你能帮助他算出每个游戏初局的最高得分。

无标题.png

 游戏规则是这样的:如上图所示所有的小球都布局在一行上,每个小球都有自己的颜色,连续摆放的同颜色小球构成一个小球片段,上图中共有14个小球片段,每个小球片段分别有:5、2、1、2、1、1、1、2、1、1、1、1、1、1个小球片段,玩家每次点击一个小球,则该小球所在的小球片段就会消失。若消失的小球片段中共有m个小球,则玩家获得m*m个积分。

请问:给定游戏开始时的状态,玩家可获得的最高积分是多少?所以说如果你不好好学习,游戏都玩不好🙃🙃🙃!

输入格式:

第一行是一个整数n(1<=n<=15),表示共有n组测试数据。
接下来每组测试数据共包括两行:
(1)第1行是一个整数k(1<=k<=200),表示共有k个小球;
(2)第2行包含k个正整数,表示每个小球的颜色这些整数的取值范围是[1,n]。

输出格式:

对每组测试数据,输出玩家可以获得的最高积分,每组数据的输出单独占一行。

输入样例:

在这里给出一组输入。例如:

3
1
1
9
1 2 2 2 2 3 3 3 1
22
1 1 1 1 2 2 2 2 1 1 1 1 1 3 3 3 2 2 1 1 1 1

输出样例:

在这里给出相应的输出。例如:

1
29
198

 这道题是一个区间DP问题。即根据每个区间的最优值求得一个大区间的最优值。即根据小区间的最优解得到大区间的最优解。

(下面是一道经典的区间DP)

在一个操场上摆放着一排 N 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 两 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。计算出将 N 堆石子合并成一堆的最小得分。

这个题目需要注意的是每次只能合成相邻的两堆石子,如果是任意两堆石子这道题就不是区间DP问题了。任意两块石头可以看洛谷P1090。

这个只能合成相邻两块石头,就可以分成区间来做,用一个二维数组dp[l][r]表示合并l到r块石头的最小的得分。那么1到n块石头首先可以通过一个中间值k分为两半,dp[l][r]表示为dp[l][k]+dp[k+1][r]+这两段石头的石子数,当然如果这段石子只有一个石堆,则它的值为0。(因为不需要合并)我们可以先算出长度为2的石子合并的最小得分数,然后再依次得到更长段的石堆的最小得分。如何得到最小呢?就是不断枚举这段石子的中间值,得出一个k,使得dp[l][k]+dp[k+1][r]+这两段石头的石子数这个数字最小。下面给出这道经典问题得实现代码:

注意这不是本道题的代码!

#include<iostream>
#include<string.h> 
using namespace std;
const int N=4000;
int dp[N][N],a[N],sum[N];
int main (){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];
	}
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++) dp[i][i]=0;
	for(int len=2;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int r=i+len-1,l=i;
			for(int k=l;k<r;k++) dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+(sum[r]-sum[l-1]));
		}
	}
	cout<<dp[1][n];
	return 0;
}

当然这个方法很有局限性,比如石堆数不能过大,因为要开二维数组。

下面我们回到消消乐这个问题。

这个也是区间DP问题,就是通过每段的最优解得到最终的最优解。但出现一个问题,就是说这段虽然是最优解,但放在整段就可能不是最优解。比如红黄红,这里面如果我们区间DP可能就直接得到单独消除这三段的得分,而实际上这道题想要得到最优解,必须先去除黄色,然后再得到红色的,这样才能使得分最大。通俗得说就是我们考虑中间得一个子段,它虽然在当时是最优解,但放在整个颜色段,它可能会有 和这个子段里面一种颜色相同的颜色,而且这两个颜色合并消除的话会得到更多分。

这两个颜色合并消除也会有代价,就是需要把这两个颜色中间的先全消除,他们才能合并消除,那么中间这段颜色就只能自身消除了,因为是两个相同的颜色夹着它,中间的颜色不能再去结合这段颜色之外的颜色,因为有开始那一对相同的颜色挡着。而结合相同的颜色条件是先消除挡着他们的颜色,我们现在就要先消除中间的颜色,就不能先消除那一对相同颜色的球了。(说的好绕,希望你能理解)。放心我们会比较,从而得出哪一些颜色合并消除好,哪一些颜色分开消除好。

这道题的输入数据是球的个数和颜色的组成,我把这些数据转化了,颜色的种类加上这些颜色的个数。因为这样才能转化为DP。

其实上面我所说的目的就是说还可以用区间DP,但这此要用三维数组,dp[l][r][k],同样l,r,表示区间的位置,k表示这个区间之后有多少个与区间第r个颜色相同球,比如dp[1][2][3]表示第一个颜色到第二个颜色再加上3个与第2个颜色相同的球合并消除所得到的最大的分数。其实带上这个k,k>0就说明我们要把这k个颜色与前面这一段最后一个颜色合并消除,得到的最大分数,因为我们如果不用合并消除的话,直接让k=0,k>0就说明我们需要合并消除。

  然后说对于每个区间段,我们只考虑最后一种颜色的球与区间之外相同的球进行合并消除,因为之前我们已经考虑过了,我们是从长度为2的段数开始考虑的。

  对于一段相同颜色的球我们有三种处理方式,

1,单独消除它

2,与它后面和它颜色相同的若干球进行消除合并

3,把这段球归到前面与它颜色相同的球那里,进行合并消除,或者再一起归到之前一段颜色相同的球那里。(这里当然是要在之前能找到)(就像是一下积累了好几大段进行合并消除)

代码里面的f数组就为dp数组

for(int k = 0; k <= suf[j]; k++){
					f[i][j][k] = max(f[i][j][k], f[i][j - 1][0] + (num[j] + k) * (num[j] + k));
				}

(这个是进行第1,2步操作)

这段代码就是我们算出这段球合并不同数量的与第j位颜色相同的球后得到的最大分数,如果k=0,说明不找颜色相同的球合并消除了,它自身消除。suf[j]表示第j种颜色球之后与它相同的颜色球的个数。至于f[i][j - 1][0],为什么这个k的位置为0,就是我前面说的合并两种相同颜色的球,他们中间那段球就只能自身合并了,因为只有他们自身消除,最后才能使两种颜色相同的球合并消除。

for(int k = i; k <= j - 2; k++)    //这里不看j-1是因为j-1的颜色不能和j的颜色相同 
				{
					if(color[k] == color[j])
					{
						for(int t = 0; t <= suf[j]; t++)
						{
							f[i][j][t] = max(f[i][j][t], f[i][k][num[j] + t] + f[k + 1][j - 1][0]);    //这里为什么是 num[j] + t,因为 k 和 j 合并了,所以 j 后面一共有 num[j] + t 个
						}
					}
				}

(这个是进行第3步操作)

这里面是找到内部与j位相同颜色的球,然后再加上j位球的数量,以及本来带的数量。f[k + 1][j - 1][0]表示消除他们中间那段颜色的球,同样只能单独消除。

到这里我们就把一段球是否合并消除的三种操作算完了,得出合并或者不合并的最大值,然后再依次递推,得出最终dp[1][n][0]的值。可能回游疑问为什么我们要求的值k为0,但中间还要求k不为零的值呢,当然是因为k为0的值可能会用到k不为0时的值。

下面是这段消消乐的代码。

#include<bits/stdc++.h>
using namespace std;
const int N = 205;
int n,t;
int color[N], num[N], suf[N], f[N][N][N],last;
int main()
{
	scanf("%d",&t);
	for(int zw=0; zw<t; zw++)
	{
		scanf("%d", &n);
		for(int i=1;i<=n;i++) num[i]=0;
		cin>>color[1];
		num[1]++;
		last=color[1];
		int tem1;
		for(int i = 1,k=1;k<n;k++)
		{
			int tem;
			cin >> tem;
			if(tem==last){
				num[i]++;
			}else{
				i++;
				color[i]=tem;
				num[i]++;
				last=tem;
			}
			tem1=i;
		}
		if(n==1){
		cout<<1<<endl;
		continue;}
		n=tem1;
		for(int i = 1; i <= n; i++)    
		{
			for(int j = i + 1; j <= n; j++)
			{
				if(color[i] == color[j])  //初始化suf 
				{
					suf[i] += num[j];
				}
			}
		}
		memset(f, 0xcf, sizeof(f));
		for(int i = 1; i <= n; i++)
		{
			for(int j = 0; j <= suf[i]; j++)//初始化长度为1的dp数组,如果长度为1,那就直接消除,如果它带了一些数字也就一起的消除 
			{
				f[i][i][j] = (num[i] + j) * (num[i] + j);  
			}
		}
		for(int len = 2; len <= n; len++)
		{
			for(int i = 1; i + len - 1 <= n; i++)
			{
				int j = i + len - 1;
				for(int k = 0; k <= suf[j]; k++){
					f[i][j][k] = max(f[i][j][k], f[i][j - 1][0] + (num[j] + k) * (num[j] + k));
				}
				for(int k = i; k <= j - 2; k++)    //这里不看j-1是因为j-1的颜色不能和j的颜色相同 
				{
					if(color[k] == color[j])
					{
						for(int t = 0; t <= suf[j]; t++)
						{
							f[i][j][t] = max(f[i][j][t], f[i][k][num[j] + t] + f[k + 1][j - 1][0]);    //这里为什么是 num[j] + t,因为 k 和 j 合并了,所以 j 后面一共有 num[j] + t 个
						}
					}
				}
			}
		}
		printf("%d\n", f[1][n][0]);
	}
	return 0;
}

这段题算是比较难的,我开始也没有思路,因为接触的dp也少,也是借鉴别人的思路。希望这些题解能对你有帮助。

最后再说一下memset就是给数组赋值,0xcf就是很大的负值,0x3f是很大的正值。

完结。

有什么问题我们也可以一块交流。

  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值