近期動態規劃總結

<1> 51nod 1055 最長等差數列
【暴力】
f[i][j] 代表以第i個元素爲等差數列的開頭,公差爲j能構成的等差數列的長度。

  • 排序

  • 枚舉每一個元素位置(i)

  • 枚舉每一個公差(j)

  • 枚舉當前元素之後位置的元素(k)
    【正解】
    得知數列的第一項和第二項便能確定公差。
    。 f[i][j]代表以第i個元素和第j個元素爲等差數列的第一個和第二個元素所能構成的最長等差數列的項數。
    f[i][j]=max{ f[j][k] +1(a[j]*2==a[i]+a[k])}

  • 排序

  • 枚舉第一個元素(i)【注意從大到小】

  • 枚舉第二個元素(j)

  • 枚舉第三個元素(k)
    【優化】
    由於單調性,用兩個指針同時遍歷數列元素。

  • 枚舉第二個元素(j)

  • 以j爲中心分別向兩端查找符合條件的i和k
    【PS】
    需要 short int
    【代碼】

#include<bits/stdc++.h>
using namespace std;
int n;
int a[10010]={};
short int f[10010][10010]={};
short int ans=2;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	 cin>>a[i];
	sort(a+1,a+n+1);
	for(int j=n-1;j>=2;j--)
	{
		int i=j-1;
		int k=j+1;
		for(;i>=1&&k<=n;)
		{
			if(a[i]+a[k]==a[j]*2)
			{
				if(f[j][k]==0)
				 f[j][k]=2;
				f[i][j]=f[j][k]+1;
				ans=max(ans,f[i][j]);
				i--;
				k++;
			}
			if(a[i]+a[k]<a[j]*2)
			 k++;
			if(a[i]+a[k]>a[j]*2)
			 i--;
		}
	}
	cout<<ans<<endl;
	return 0;
 } 

<2>51nod 1052 最大M子段和
【正解】
f[i][j]代表已經訪問前i個元素,共分成j組的最大子段和(第i個元素必須取)
f[i][j]=max{ f[i][j-1]+a[i] , f[k][j-1]+a[j] }
【優化】
更新+滾動數組
【代碼】

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[5001]={};
long long f[5001]={};
long long p[5001]={};
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	 cin>>a[i];
	for(int j=1;j<=m;j++)
	{
	    for(int i=j;i<=n;i++)
	    {
			f[i]=f[i-1]+a[i];
			f[i]=max(f[i],p[i-1]+a[i]);
			if(i-2>=0)
			{
			    p[i-1]=f[i-2];
	            p[i-1]=max(p[i-1],p[i-2]);
	        }
	    }
	} 
	long long ans=0;
	for(int i=m;i<=n;i++)
	 ans=max(ans,f[i]);
	cout<<ans<<endl;
	return 0;
 } 

<3>51nod 1270 數組的最大代價
【暴力】
f[i][j]表示第i個元素取j的最大代價。
【正解】
f[i][0]表示當第i個元素取最小值(1)時的最大代價。
f[i][1]表示當第i個元素取最大值(b[i])時的最大代價。
f[i][0]=max{f[i-1][0],f[i-1][1]+b[i-1]-1}
f[i][1]=max{f[i-1][0]+b[i]-1,f[i-1][1]+abs(b[i]-b[i-1])
【代碼】

#include<bits/stdc++.h>
using namespace std;
int n;
int b[50010]={};
int f[2][2]={};
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	 cin>>b[i];
	for(int i=2;i<=n;i++)
	{
		f[i%2][0]=max(f[(i-1)%2][0],f[(i-1)%2][1]+b[i-1]-1);
		f[i%2][1]=max(f[(i-1)%2][0]+b[i]-1,f[(i-1)%2][1]+abs(b[i]-b[i-1]));
	}
	cout<<max(f[n%2][0],f[n%2][1])<<endl;
	return 0;
}

小結:

  • Dp最重要的是dp數組所代表的意義和轉移方程。
  • 要注意dp數組的範圍查找對應的變量。
  • 暴力不可怕,可怕的是連暴力都沒想出來。
  • 成功的路上也許只差一個線性優化。
  • 或許離成功只有一個拐角——一張草稿。
  • 列表很重要。
  • ……(未完待續)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章