HDU 4283 You Are the One(区间dp)

来源:互联网 发布:java颗粒度是什么意思 编辑:程序博客网 时间:2024/06/05 09:29
//区间dp的用法真是灵活
//这个题 想了很长时间 还是错了
//下面的思路是参照大牛ZeroClock的
// 思路: 栈有一个重要的性质就是 假设共有[1,n]元素 如果第一个元素第k个出栈则 元素2到k比第一个元素先出栈 元素k+1到n比第一个元素后出栈
// 那么[1,n]就被分成了[2,k] [k+1,n]两个区间这样就可以用区间dp的三重循环的去做了
//第一重循环i表示区间的起点 第二重循环j表示区间的终点  第三重循环k表示区间[i,j]的第一个元素i 第k个出栈
// 则 dp[i][j]=dp[i+1][k]+dp[k+1][j]+s;
// s为当前选择对后续的影响  应该提前加到当前的状态了  


#include<stdio.h>
#include<string.h>


#define N 105
#define inf 99999999
int dp[N][N];


int a[N], sum[N];




int min(int a,int b){return a<b?a:b;}
void init(int n)
{
int i ,j;
memset(sum,0,sizeof(sum));
memset(dp,0,sizeof(dp));


for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)//因为j<i是非法状态所以值为0
{
dp[i][j]=inf;
}
}


int solve_dp(int n)
{
int i,j,k,s;  //dp[i][j]表示的是只要区间[i,j]时最佳出栈方式的总屌丝值
for(i=n;i>=1;i--)//枚举区间起点
{
for(j=i;j<=n;j++)//枚举区间终点
{
for(k=i;k<=j;k++)//枚举元素i是第k个出栈
{
if(i==j)continue;
s=a[i]*(k-i);
s+=dp[i+1][k]+dp[k+1][j];
s+=(k-i+1)*(sum[j]-sum[k]);
dp[i][j]=min(dp[i][j],s);
}
}
}
return dp[1][n];
}


int main()
{


int t,n,X=1;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init(n);
int ans=solve_dp(n);
printf("Case #%d: %d\n",X++,ans);


}
return 0;

}

记忆化搜索方法(转自ZeroClock)始终没看懂!

本题的模型是求一个合法的出栈序列使得屌丝总值最小,需要用区间DP解决之。合法的出栈序列中有一个很重要的性质:[1,n]这是一开始的所有元素,当1第k个出栈时[2,k]肯定比1先出栈,[k+1,n]肯定比1后出栈,正因为只要1才能第k个出栈.。这样一个区间划分成两个子区间[2,k],[k+1,n],就这样递归下去只到区间长度为1.而区间DP正是解决这类区间划分问题的利器,其实区间DP也就是一种思想啦。

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define MAX 104  
  4. #define INF (1<<29)  
  5. #define min(a,b) ((a)<(b)?(a):(b))  
  6.   
  7.   
  8. int n,ans,arr[MAX];  
  9. int dp[MAX][MAX][MAX];  
  10.   
  11.   
  12. int Solve_DP(int s,int e,int k) {  
  13.     //s是区间头,e是区间尾,k表示第i个人第k个出栈  
  14.     if (s == e) return arr[s] * (k - 1);  
  15.     if (s > e)  return 0;  
  16.     if (dp[s][e][k] != INF) return dp[s][e][k];   
  17.   
  18.       
  19.       
  20.     int i,first,second;  
  21.     int cur,thisk,nextk;  
  22.     for (i = s; i <= e; ++i) {                   //区间分成两部分,[s,i]和[i+1,e]  
  23.           
  24.         nextk = k + (i - s) + 1;                //[i+1,e]区间内第i个人第nextk个出栈  
  25.         thisk = k + (i - s);                    //[s,i]区间的第s个人第thisk个出栈  
  26.         first = Solve_DP(s+1,i,k);              //第1个区间经过分配得到最少屌丝值  
  27.         second = Solve_DP(i+1,e,nextk);         //第2个区间经过分配得到的最少屌丝值  
  28.         cur = arr[s] * (thisk - 1);             //第s个人第thisk出栈增加的屌丝值  
  29.         dp[s][e][k] = min(dp[s][e][k],first+second+cur);  
  30.     }  
  31.   
  32.   
  33.     return dp[s][e][k];  
  34. }  
  35.   
  36.   
  37. int main()  
  38. {  
  39.     int i,j,k,t,cas = 0;  
  40.       
  41.       
  42.     scanf("%d",&t);  
  43.     while (t--) {  
  44.           
  45.         scanf("%d",&n);  
  46.         for (i = 1; i <= n; ++i)  
  47.             scanf("%d",&arr[i]);  
  48.           
  49.           
  50.         for (i = 1; i <= n; ++i)  
  51.             for (j = 1; j <= n; ++j)  
  52.                 for (k = 0; k < n; ++k)  
  53.                     dp[i][j][k] = INF;  
  54.                   
  55.                   
  56.         ans = Solve_DP(1,n,1);  
  57.         printf("Case #%d: %d\n",++cas,ans);  
  58.     }  
  59. }  

0 0