[SCOI2005]最大子矩陣解題報告
阿新 • • 發佈:2018-12-10
[SCOI2005]最大子矩陣 解題報告
題目描述
這裡有一個n*m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。注意:選出的k個子矩陣不能相互重疊。
輸入輸出格式
輸入格式
第一行為n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下來n行描述矩陣每行中的每個元素的分值(每個元素的分值的絕對值不超過32767)。
輸出格式:
只有一行為k個子矩陣分值之和最大為多少。
input
3 2 2 1 -3 2 3 -2 3
output
9
思路:
顯然這道題是一道與矩陣有關的DP
所以可以用一種叫 懸線法 的奇技淫巧
所謂懸線法就是先把每一行,每一列的狀態都給拓展出來,然後再變為矩陣進行合併
先求出字首和,方便轉移。 然後就是轉移了。
wait!!
有沒有注意到m<=2?? 當m=1時,很簡單。。
f[i][k]=max(f[i][k],f[j][k-1]+sum[1][i]-sum[1][j]);//代表在前i個數中
//分成k個所取得的最大值
當m=2時
就要用懸線法啦!!
F[i][j][k]代表在第一行取i個,第二行取個,分成k個的最大值。
什麼都不做
先拓展第一行
然後第二行
最後合併
for(int k=1;k<=K;k++)
for(int i=1;i<=n;i++)
for(int j=1; j<=n;j++)
{
F[i][j][k]=max(F[i-1][j][k],F[i][j-1][k]);//不在這一個點取數
for(int l=0;l<i;l++) //拓展第一行
F[i][j][k]=max(F[i][j][k],F[l][j][k-1]+sum[1][i]-sum[1][l]);
for(int l=0;l<j;l++)//拓展第二行
F[ i][j][k]=max(F[i][j][k],F[i][l][k-1]+sum[2][j]-sum[2][l]);
if(i==j)//i==j時就是一個矩陣啦,要合併了。
for(int l=0;l<i;l++)
F[i][j][k]=max(F[i][j][k],
F[l][l][k-1]+sum[1][i]-sum[1][l]+sum[2][j]-sum[2][l]);
}
應該都懂了吧??
完整程式碼
#include<bits/stdc++.h>
using namespace std;
#define maxn 105
#define INF 0x3f3f3f
int n,m,K,x;
int f[maxn][maxn];
int F[maxn][maxn][maxn];
int sum[4][maxn];
int main()
{
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&x),sum[j][i]=sum[j][i-1]+x;//字首和
if(m==1)
{
//memset(f,-INF,sizeof(f));
for(int k=1;k<=K;k++)
for(int i=1;i<=n;i++)
{
f[i][k]=f[i-1][k];
for(int j=0;j<i;j++)
f[i][k]=max(f[i][k],f[j][k-1]+sum[1][i]-sum[1][j]);
}
printf("%d\n",f[n][K]);
return 0;
}
else
{
//memset(F,-INF,sizeof(F));
for(int k=1;k<=K;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
F[i][j][k]=max(F[i-1][j][k],F[i][j-1][k]);
for(int l=0;l<i;l++)
F[i][j][k]=max(F[i][j][k],F[l][j][k-1]+sum[1][i]-sum[1][l]);
for(int l=0;l<j;l++)
F[i][j][k]=max(F[i][j][k],F[i][l][k-1]+sum[2][j]-sum[2][l]);
if(i==j)
for(int l=0;l<i;l++)
F[i][j][k]=max(F[i][j][k],
F[l][l][k-1]+sum[1][i]-sum[1][l]+sum[2][j]-sum[2][l]);
}
printf("%d\n",F[n][n][K]);
}
return 0;
}
祝各位dalao成功AC!!!