2017.10.20 T2 1998
題目背景
SCOI2005
bzoj1084
題目描述
這裏有一個n*m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。注意:選出的k個子矩陣不能相互重疊。
輸入格式
第一行爲n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下來n行描述矩陣每行中的每個元素的分值(每個元素的分值的絕對值不超過32767)。
輸出格式
只有一行爲k個子矩陣分值之和最大爲多少。
樣例數據
輸入
3 2 2
1 -3
2 3
-2 3
輸出
9
分析:考場上除了只選一個矩陣和只有正值的矩陣的數據之外好像都沒什麼思路,特判一些情況好像並沒有分。正確思路其實已經想到了(因爲只有兩列!這不是明擺着爲了dp減少情況嘛),但是dp的細節太多了,時間上不划算,決定打完暴力+特判就去看T3,然後T3離奇暴力AC233333。
一列的就不說了,兩列的每行dp有如下幾種情況:
1、都不加
2、只在左邊一列加一個
3、只在右邊一列加一個
4、兩列各加一個,但是是分開的兩個
5、兩列各加一個,構成連着的一個
然後就是直接dp[i][k][0/1/2/3/4],表示第i行時選了k個矩陣是上面1、2、3、4、5哪種情況,這樣只需掃一遍行,複雜度O(
代碼
賦負值讓我很難受,還有一堆邊界……
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
int N,M,K,ans;
int map[110][110],dp[110][15][5];
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
ans=-200000000;//ans賦負值
N=getint(),M=getint(),K=getint();
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
map[i][j]=getint();
if(M==1)//只有一列時
{
for(int k=1;k<=K;++k)
for(int i=1;i<=N;++i)
{
dp[i][k][0]=-0x3f3f3f3f;//賦負值
dp[i][k][1]=-0x3f3f3f3f;
if(i-1>=k)//邊界,前面至少要有k個,不然就不存在dp[i-1][k],也就不用更新
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]);//這個不選,就只從前面賦值過來
if(i>=k)//同理
dp[i][k][1]=max(dp[i-1][k][1],max(dp[i-1][k-1][0],dp[i-1][k-1][1]))+map[i][1];//這個選,如果前一個也有那麼可以選擇合併成一個或者不合並也就是矩陣+1個
}
for(int i=1;i<=N;++i)
for(int j=0;j<=1;++j)
ans=max(ans,dp[i][K][j]);//找到答案
}
else
{
dp[1][0][0]=-0x3f3f3f3f;//賦負值
dp[1][1][1]=map[1][1];
dp[1][1][2]=map[1][2];
dp[1][2][3]=map[1][1]+map[1][2];
dp[1][1][4]=map[1][1]+map[1][2];
for(int k=1;k<=K;++k)
for(int i=1;i<=N;++i)
{
dp[i][k][0]=-0x3f3f3f3f;//賦負值
dp[i][k][1]=-0x3f3f3f3f;
dp[i][k][2]=-0x3f3f3f3f;
dp[i][k][3]=-0x3f3f3f3f;
dp[i][k][4]=-0x3f3f3f3f;
if(i*2-2>=k)//邊界,同理
dp[i][k][0]=max(dp[i-1][k][0],max(dp[i-1][k][1],max(dp[i-1][k][2],max(dp[i-1][k][3],dp[i-1][k][4]))));//不選就從前一行的所有情況更新
if(i*2-1>=k)//同理
{
dp[i][k][1]=max(dp[i-1][k][1],max(dp[i-1][k][3],max(dp[i-1][k-1][0],max(dp[i-1][k-1][1],max(dp[i-1][k-1][2],max(dp[i-1][k-1][3],dp[i-1][k-1][4]))))))+map[i][1];//在左側一列加一個,可以是前一行的所有情況+1個矩陣,也可以和前一行(如果左側一列也有單獨的矩陣的話)合成同一個矩陣;右側一列是一樣的
dp[i][k][2]=max(dp[i-1][k][2],max(dp[i-1][k][3],max(dp[i-1][k-1][0],max(dp[i-1][k-1][1],max(dp[i-1][k-1][2],max(dp[i-1][k-1][3],dp[i-1][k-1][4]))))))+map[i][2];
}
if(i*2>=k)//同理
{
if(k>=2)//要使k-2存在(不爆負數纔不爆數組)
dp[i][k][3]=max(dp[i-1][k][3],max(dp[i-1][k-2][0],max(dp[i-1][k-2][1],max(dp[i-1][k-2][2],max(dp[i-1][k-2][3],max(dp[i-1][k-2][4],max(dp[i-1][k-1][1],max(dp[i-1][k-1][2],dp[i-1][k-1][3]))))))))+map[i][1]+map[i][2];//加兩個單獨的情況較多,是左側加一個和右側加一個的結合
dp[i][k][4]=max(dp[i-1][k][4],max(dp[i-1][k-1][0],max(dp[i-1][k-1][1],max(dp[i-1][k-1][2],max(dp[i-1][k-1][3],dp[i-1][k-1][4])))))+map[i][1]+map[i][2];//加一個合併的,可以前面的所有情況+1,也可以是和前面的矩陣(如果上一行也是合併的矩陣的話)合成一個矩陣
}
}
for(int i=1;i<=N;++i)
for(int j=0;j<=4;++j)
ans=max(ans,dp[i][K][j]);//找到答案
}
cout<<ans<<'\n';
return 0;
}
本題結。