HDU4571-区间DP,记忆化搜索,递推

来源:互联网 发布:c++ gui qt 4编程 编辑:程序博客网 时间:2024/05/16 02:20

F
给定一个数组,把这个数组分成n段,
每一份的值为 第一个数 a*pow(2,i),i为段的长度。
用记忆化搜索
dp[i]=min(dp[i],dp[x]+a[i]*1<<(x-i))
x-i为i为头的段的长度,首为i,尾为x-1;
x为新段的长度。
方法1 记忆话搜索
这个最好理解了,但是最重要的是对x的控制。
和边界的处理。

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;typedef long long ll;const int maxn=70;ll dp[maxn];ll a[maxn];int n;int t;int dfs(int x){  if(x>n) return 0;   if(dp[x]!=-1) return dp[x];   dp[x]=1e19;   /*i是下一段的初始值。   进行当前合法段的搜索   1 长度不得超过20,i+19正好就是20;   2 长度不得过n。   */   for(int i=x+1;i<=min(x+20,n+1);i++)   {  dp[x]=min(dp[x],dfs(i)+a[x]*(1<<i-x));   }   //cout<<dp[x]<<endl;    return dp[x];}int main(){    scanf("%d",&t);   while(t--)   {   scanf("%d",&n);     memset(dp,-1,sizeof(dp));      ll sum=0;      for(int i=1;i<=n;i++)      {  scanf("%lld",&a[i]);         sum+=a[i]*2;      }      ll sum1=dfs(1);      ll ans=min(sum1,sum);      cout<<ans<<endl;   }    return 0;}

方法2 区间dp,二维的也好理解。
类似的 松弛操作的区间dp的典型操作。。

#include <iostream>#include <cstdio>#include <cstdlib>using namespace std;/*给定你一个数组,分为若干份,每份大小不得大于20要求是 每一份最大不能超过20;方法1 dp[i][j]为i到j(不包括j)这个区间的最大值。dp[i][j]=dp[i][k]+dp[i][k+1][j]k作为一个分段,当前的dp保存的是上一个区间的大小。要注意初始化。1712445432 10000 10000 10000 10000 10000 1000010000 4 10000 10000 10000 10000 1000010000 10000 8 10000 10000 10000 1000010000 10000 10000 8 10000 10000 1000010000 10000 10000 10000 10 10000 1000010000 10000 10000 10000 10000 8 1000010000 10000 10000 10000 10000 10000 6*************2 4 10000 10000 10000 10000 1000010000 4 8 10000 10000 10000 1000010000 10000 8 16 10000 10000 1000010000 10000 10000 8 16 10000 1000010000 10000 10000 10000 10 18 1000010000 10000 10000 10000 10000 8 1410000 10000 10000 10000 10000 10000 6*************2 4 8 10000 10000 10000 1000010000 4 8 16 10000 10000 1000010000 10000 8 16 24 10000 1000010000 10000 10000 8 16 24 1000010000 10000 10000 10000 10 18 2410000 10000 10000 10000 10000 8 1410000 10000 10000 10000 10000 10000 6*************2 4 8 16 10000 10000 1000010000 4 8 16 24 10000 1000010000 10000 8 16 24 32 1000010000 10000 10000 8 16 24 3010000 10000 10000 10000 10 18 2410000 10000 10000 10000 10000 8 1410000 10000 10000 10000 10000 10000 6*************2 4 8 16 24 10000 1000010000 4 8 16 24 32 1000010000 10000 8 16 24 32 3810000 10000 10000 8 16 24 3010000 10000 10000 10000 10 18 2410000 10000 10000 10000 10000 8 1410000 10000 10000 10000 10000 10000 6*************2 4 8 16 24 32 1000010000 4 8 16 24 32 3810000 10000 8 16 24 32 3810000 10000 10000 8 16 24 3010000 10000 10000 10000 10 18 2410000 10000 10000 10000 10000 8 1410000 10000 10000 10000 10000 10000 6*************2 4 8 16 24 32 3810000 4 8 16 24 32 3810000 10000 8 16 24 32 3810000 10000 10000 8 16 24 3010000 10000 10000 10000 10 18 2410000 10000 10000 10000 10000 8 1410000 10000 10000 10000 10000 10000 6*************38*/const int maxn=2000;typedef long long ll;ll dp[maxn][maxn];ll a[maxn];int t;int m;int main(){   ios::sync_with_stdio(false);    cin>>t;    while(t--)    {   cin>>m;    for(int i=1;i<=m;i++)         cin>>a[i];        for(int i=1;i<=m;i++)            for(int j=1;j<=m;j++)             dp[i][j]=1e19;         for(int i=0;i<m;i++)         {  for(int j=1;j<=m-i;j++)             {   if(i<20)                 dp[j][j+i]=a[j]*(1<<i+1);                 for(int k=j;k<j+i;k++)                    dp[j][j+i]=min(dp[j][k]+dp[k+1][j+i],dp[j][j+i]);             }              /*for(int xx=1;xx<=m;xx++)                    {for(int yy=1;yy<=m;yy++)                     cout<<dp[xx][yy]<<" ";                     cout<<endl;                    }                    cout<<"*************"<<endl;*/         }         //cout<<dp[1][5]<<endl;          //cout<<dp[5][7]<<endl;   cout<<dp[1][m]<<endl;    }    return 0;}

3 递推,递推的方法和第四个不一样。
dp[i]为从1到i的最优解,
要知道,当为j时,最后一段首尾 i-j+1.

#include <iostream>#include <cstdio>#include <cstdlib>using namespace std;typedef long long ll;ll dp[70];ll a[70];int main(){    int t;    int n;    cin>>t;    while(t--)    {   cin>>n;    long long sum=0;      for(int i=1;i<=n;i++)         {cin>>a[i];          sum+=a[i]*2;         }       for(int i=1;i<=n;i++)       {  dp[i]=1e19;           for(int j=1;j<=i;j++)          {  if(j>20)break;              dp[i]=min(dp[i-j]+a[i-j+1]*(1ll<<j),dp[i]);          }       }       ll ans=min(sum,dp[n]);       cout<<ans<<endl;    }    return 0;}

4 这个和 记忆化搜索几乎一模一样。
记忆化搜索也有dp[n]的处理,只不过在递归的过程中自己执行了。

#include<iostream>#include<cstdio>#include<list>#include<algorithm>#include<cstring>#include<string>#include<queue>#include<stack>#include<map>#include<vector>#include<cmath>#include<memory.h>#include<set>#define ll long long#define eps 1e-8#define inf 0xfffffffconst ll INF = 1ll<<61;using namespace std;//vector<pair<int,int> > G;//typedef pair<int,int > P;//vector<pair<int,int> > ::iterator iter;////map<ll,int >mp;//map<ll,int >::iterator p;ll num[100 + 56];ll dp[100 + 56];void init() {    memset(num,0,sizeof(num));    memset(dp,0,sizeof(dp));}int main() {    int t;    scanf("%d",&t);    while(t--) {        init();        int n;        scanf("%d",&n);        ll ans = 0ll;        for(int i=1;i<=n;i++) {            scanf("%I64d",#[i]);            ans += 2 * num[i];//分n段        }        dp[n] = 2 * num[n];        for(int i=n-1;i>0;i--) {            dp[i] = INF;            for(int j=i+1;j<=n+1;j++) {                if(j > i + 20)break;                dp[i] = min(dp[i],dp[j] + num[i] * (1ll<<j - i));            }        }        printf("%I64d\n",min(ans,dp[1]));    }    return 0;}
阅读全文
0 0
原创粉丝点击