POJ - 1050 To the Max(最大连续子段和,线性dp)
題目鏈接:點(diǎn)擊查看
題目大意:給出一個(gè)n*n的矩陣,每個(gè)點(diǎn)都有一個(gè)權(quán)值,現(xiàn)在要從中選取一個(gè)子矩陣要求權(quán)值和最大,問這個(gè)最大權(quán)值和是多少
題目分析:因?yàn)槭且笞泳仃嚨臋?quán)值和最大的問題,我們可以直接維護(hù)一個(gè)二維前綴和,然后n^4暴力求解即可,簡(jiǎn)單暴力不多說了,著重說一下另一個(gè)思想
上面的4層for分別枚舉的是行起點(diǎn),行終點(diǎn),列起點(diǎn),列終點(diǎn),我們可以通過線性dp將列起點(diǎn)和列終點(diǎn)這兩層for優(yōu)化成一層for,具體的實(shí)現(xiàn)就是求一個(gè)最大連續(xù)子段和,我們可以將行起點(diǎn)到行終點(diǎn)中的所有行對(duì)應(yīng)的列累加到一個(gè)一維數(shù)組中,然后求一下這個(gè)數(shù)組的最大連續(xù)子段和,就是當(dāng)前枚舉的行之中最大的答案了,實(shí)時(shí)維護(hù)一下最大值就好了
就是突然感覺,最大連續(xù)子段和的轉(zhuǎn)移方程一下子就能自己寫出來了,感覺和前兩天做的那個(gè)樹形dp很像,每個(gè)點(diǎn)都有兩種選擇,一種選擇是接在前面的后面,另一種選擇是自己重新當(dāng)新的開頭,當(dāng)然如果前置狀態(tài)對(duì)當(dāng)前的貢獻(xiàn)為正,肯定是接在前面的后面更優(yōu),但如果前置的狀態(tài)為負(fù),當(dāng)然還是自己獨(dú)成一個(gè)開頭更優(yōu),實(shí)現(xiàn)很簡(jiǎn)單,有注釋看起來應(yīng)該不難
還有一種實(shí)現(xiàn)方法,更加廣義一點(diǎn),我們可以上升為求子段長度不小于L的最大連續(xù)子段和,這個(gè)時(shí)候我們就需要借助前綴和來實(shí)現(xiàn)了,轉(zhuǎn)移方程就是dp[i]=sum[i]-min(sum[j]),j=[0,i-L],乍一看,時(shí)間復(fù)雜度是n*n級(jí)別的,其實(shí)不然,后面求最小值的那個(gè)部分還可以繼續(xù)優(yōu)化,我們不妨用一個(gè)變量mmin直接維護(hù)一下前i-L中的最小值,這樣就能將時(shí)間復(fù)雜度優(yōu)化至O(n)了,具體實(shí)現(xiàn)也很簡(jiǎn)單:
for(int i=L;i<=n;i++)
{
? ? mmin=min(mmin,sum[i-L]);
? ? ans=max(ans,sum[i]-mmin);
}
代碼:
1:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;int n;int maze[N][N];//維護(hù)最初的矩陣int sum[N];//維護(hù)的一維矩陣int solve()//最大連續(xù)子段和 {int ans=-inf;//答案int temp=0;//當(dāng)前的子段和for(int i=1;i<=n;i++){if(temp<0)//不要前面的 temp=sum[i];else//要前面的 temp+=sum[i];ans=max(ans,temp);//實(shí)時(shí)更新答案}return ans; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&maze[i][j]);int ans=-inf;for(int i=1;i<=n;i++)//行起點(diǎn) {memset(sum,0,sizeof(sum));for(int j=i;j<=n;j++)//行終點(diǎn) {for(int k=1;k<=n;k++)sum[k]+=maze[j][k];ans=max(ans,solve());}}printf("%d\n",ans);}return 0; }2:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;int n;int maze[N][N];int sum[N];int summ[N];int solve() {int ans=-inf;int temp=0;for(int i=1;i<=n;i++)//ssum維護(hù)sum的前綴和summ[i]=summ[i-1]+sum[i];int mmin=inf;for(int i=0;i<=n;i++){mmin=min(mmin,summ[i]);ans=max(ans,summ[i]-mmin);}return ans; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&maze[i][j]);int ans=-inf;for(int i=1;i<=n;i++)//行起點(diǎn) {memset(sum,0,sizeof(sum));for(int j=i;j<=n;j++)//行終點(diǎn) {for(int k=1;k<=n;k++)sum[k]+=maze[j][k];ans=max(ans,solve());}}printf("%d\n",ans);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的POJ - 1050 To the Max(最大连续子段和,线性dp)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CH - 0304 IncDec Seq
- 下一篇: POJ - 2018 Best Cow