问题描述
有n个矩阵,大小分别为a0*a1, a1*a2, a2*a3, ..., a[n-1]*a[n],现要将它们依次相乘,只能使用结合率,求最少需要多少次运算。
两个大小分别为p*q和q*r的矩阵相乘时的运算次数计为p*q*r。
输入格式
输入的第一行包含一个整数n,表示矩阵的个数。
第二行包含n+1个数,表示给定的矩阵。
输出格式
输出一个整数,表示最少的运算次数。
样例输入
3
1 10 5 20
样例输出
150
数据规模和约定
1<=n<=1000, 1<=ai<=10000。
解析:
首先:
简单的动态规划 :
m[i][j]=m[i][k]+m[k+1][j]+a[i]*a[k+1]*a[j+1];
m[i][j]:决策变量 矩阵i乘到 j的最小乘的次数
i ,j 状态转移变量
a[i]:矩阵i的行数,a[i+1]是矩阵i的列数,因为如果两个矩阵要想相乘,那么前矩阵的列数必定等于后矩阵的行数
其次:
1<=ai<=10000,而我们可以看出状态转移方程式中有a[i]*a[k+1]*a[j+1],其值最大为1e12 显然大于int类型,这里要特别指出在c++和java中,如果出现long long (c++)或者long (java)(都是64位) temp=10000*10000*10000 右边的乘号的值默认保存在int中,然后赋值给temp,这就导致temp!=1e12而是一个随机数。因而a[]数组要设为long long 或 long
接着上面的,java中 long不能直接赋值 如long temp=1000000000000 会报错,原因同上,可以通过long inf=10000 ;long INF=inf*inf*inf的方式赋值。
c++代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
long long dp(long long a[],int n)
{
long long m[n+5][n+5];//m[i][j] 从矩阵i乘到矩阵j需要的最小乘数
long long INF=1e15+5;//10000*10000*10000*1000
for(int i=0;i<n;i++)//初始化
{
for(int j=0;j<n;j++)
m[i][j]=INF;
m[i][i]=0;
}
for(int len=1;len<n;len++)//连乘的矩阵位置之差
{
for(int i=0;i<n-len;i++)
{
int j=i+len;
for(int k=i;k<j;k++)
{
long long t=m[i][k]+m[k+1][j]+a[i]*a[k+1]*a[j+1];
//将[i,j)之间的矩阵依次当做中间矩阵
if(t<m[i][j])//如果t<m[i][j],更新
m[i][j]=t;
//本来还得加个标记s[i][j]=k,因为只是求最小值而非打印出来具体情况 所以省了
}
}
}
return m[0][n-1];
}
//矩阵连乘 动态规划
int main(){
int n;
cin>>n;
long long a[1005];
for(int i=0;i<n+1;i++)
cin>>a[i];//行数
cout<<dp(a,n)<<endl;
return 0;
}
测试情况:
1 | 正确 | 10.00 | 0ms | 948.0KB | 输入 输出 |
2 | 正确 | 10.00 | 0ms | 948.0KB | VIP特权 |
3 | 正确 | 10.00 | 0ms | 1.0MB | VIP特权 |
4 | 正确 | 10.00 | 15ms | 1.238MB | VIP特权 |
5 | 正确 | 10.00 | 46ms | 1.625MB | VIP特权 |
6 | 正确 | 10.00 | 109ms | 2.167MB | VIP特权 |
7 | 正确 | 10.00 | 218ms | 2.867MB | VIP特权 |
8 | 正确 | 10.00 | 734ms | 4.707MB | VIP特权 |
9 | 正确 | 10.00 | 1.093s | 5.859MB | VIP特权 |
10 | 正确 | 10.00 | 2.421s | 8.621MB | VIP特权 |
java代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
static Long dp(long a[],int n)
{
long m[][]=new long[n][n];//m[i][j] 从矩阵i乘到矩阵j需要的最小乘数
long inf=10000;
long INF=inf*inf*inf*inf/10+5;
for(int i=0;i<n;i++)//初始化
{
Arrays.fill(m[i], INF);
m[i][i]=0;
//System.out.println(INF);
}
for(int len=1;len<n;len++)//连乘的矩阵位置之差 有点floyd算法的感觉
{
for(int i=0;i<n-len;i++)
{
int j=i+len;
for(int k=i;k<j;k++)
{
long t=m[i][k]+m[k+1][j]+a[i]*a[k+1]*a[j+1];
//将[i,j)之间的矩阵依次当做中间矩阵
if(t<m[i][j])//如果t<m[i][j],更新
m[i][j]=t;
//本来还得加个标记s[i][j]=k,因为只是求最小值而非打印出来具体情况 所以省了
}
}
}
return m[0][n-1];
}
//矩阵连乘 动态规划
public static void main(String[] args) throws IOException {
int n;
BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
//加快输入
String str=bfr.readLine();
String s[]=str.split(" ");
n=Integer.parseInt(s[0]);
long a[]=new long[n+1];
str=bfr.readLine();
s=str.split(" ");
for(int i=0;i<n+1;i++)
a[i]=Long.parseLong(s[i]);//行数
System.out.println(dp(a,n));
}
}
评测详情:
1 | 正确 | 10.00 | 187ms | 19.13MB | 输入 输出 |
2 | 正确 | 10.00 | 125ms | 19.03MB | VIP特权 |
3 | 正确 | 10.00 | 156ms | 19.35MB | VIP特权 |
4 | 正确 | 10.00 | 265ms | 19.97MB | VIP特权 |
5 | 正确 | 10.00 | 265ms | 20.5MB | VIP特权 |
6 | 正确 | 10.00 | 343ms | 21.08MB | VIP特权 |
7 | 正确 | 10.00 | 312ms | 21.67MB | VIP特权 |
8 | 正确 | 10.00 | 812ms | 23.5MB | VIP特权 |
9 | 正确 | 10.00 | 1.468s | 24.50MB | VIP特权 |
10 | 运行超时 | 0.00 | 运行超时 | 27.53MB | VIP特权 |
感受:
1.以后做题要考虑边界条件,对于数据范围要敏感一点,要不好难满分啊!!!
2.java虽然功能强大,但却是效率没有c++高。android与ios之战??? 哈哈哈