HDU4960Another OCD Patient
Posted 蒟蒻LQL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU4960Another OCD Patient相关的知识,希望对你有一定的参考价值。
题意
给出一个长度为n的整数序列。可以将一段连续的序列进行合并。合并的长度不同代价不同。问付出最少多少代价可以将这个序列变成一个对称的序列。n<=5000
分析
一看题感觉是个dp很好写啊。f[i][j]为令区间[i,j]对称的最小花费。那么转移并不难想
f[i][j]=min(f[i][j],f[i+l1][j-l2]+val[l1-i+1]+val[j-l2+1] | sum[l1]-sum[i-1]==sum[j]-sum[l2-1]);
然后按照区间dp的写法就写了一个迭代然后T掉了。看了一下这样好像是n^3的?下面是T的代码
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <vector> 5 using namespace std; 6 typedef long long LL; 7 const int inf = 0x3f3f3f3f; 8 const int MAX = 5000+10; 9 int dp[MAX][MAX],a[MAX],v[MAX]; 10 LL sum[MAX]; 11 int dfs(int L,int R) { 12 if(L>=R) return 0; 13 if(~dp[L][R]) return dp[L][R]; 14 LL sum1,sum2; int ans=a[R-L+1]; 15 for(int i=L,j=R;i<j;) { 16 sum1=sum[i]-sum[L-1]; 17 sum2=sum[R]-sum[j-1]; 18 if(sum1==sum2) { 19 ans=min(ans,dfs(i+1,j-1)+a[i-L+1]+a[R-j+1]); 20 i++; j--; 21 } 22 else if(sum1>sum2) j--; 23 else i++; 24 } 25 return dp[L][R]=ans; 26 } 27 int main() { 28 int n; 29 while(scanf("%d",&n)==1&&n) { 30 memset(sum,0,sizeof(sum)); 31 for(int i=1;i<=n;i++) { 32 scanf("%d",&v[i]); 33 sum[i]=sum[i-1]+v[i]; 34 } 35 for(int i=1;i<=n;i++) { 36 scanf("%d",&a[i]); 37 } 38 memset(dp,-1,sizeof(dp)); 39 int ans=dfs(1,n); 40 printf("%d\n",ans); 41 }
然后我去网上查了一下,发现这个思想用记忆搜索可以过。。。emmm想一下,貌似是啊,记忆搜索是n^2logn?下面是记忆搜索的代码
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=5000+100; 8 const int INF=2147000000; 9 int a[maxn],val[maxn],f[maxn][maxn]; 10 int n; 11 long long sum[maxn]; 12 int dfs(int L,int R){ 13 if(f[L][R]!=-1) 14 return f[L][R]; 15 f[L][R]=val[R-L+1]; 16 for(int i=L,j=R;i<j;){ 17 long long sum1=sum[i]-sum[L-1]; 18 long long sum2=sum[R]-sum[j-1]; 19 if(sum1==sum2){ 20 f[L][R]=min(f[L][R],dfs(i+1,j-1)+val[i-L+1]+val[R-j+1]); 21 i++,j--; 22 } 23 else if(sum1>sum2)j--; 24 else i++; 25 } 26 return f[L][R]; 27 } 28 int main(){ 29 while(scanf("%d",&n)!=EOF&&n){ 30 sum[0]=0; 31 for(int i=1;i<=n;i++){ 32 scanf("%d",&a[i]); 33 sum[i]=sum[i-1]+a[i]; 34 } 35 for(int i=1;i<=n;i++) 36 scanf("%d",&val[i]); 37 memset(f,-1,sizeof(f)); 38 int ans=dfs(1,n); 39 printf("%d\n",ans); 40 } 41 return 0; 42 }
然后又开始想迭代该怎么写···
先按照上面的方法预处理出一个pos[maxn]数组,j=pos[i],sum[i]=sum[n]-sum[j-1]。也就是说1-i的和等于pos[i]到n的和。
设一个dp数组f[i]为将1-i和将pos[i]到n合并的最小花费是多少。
转移:f[i]=min(f[i],f[j]+val[i-j]+val[pos[j]-pos[i]])
最终答案是 ans=min(f[i]+val[pos[i]-i-1]);
恩,就是这样。下面是AC代码
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 const int INF=2147480000; 8 const int maxn=5000+100; 9 int a[maxn],val[maxn],f[maxn],pos[maxn]; 10 long long sum[maxn]; 11 12 int n; 13 int main(){ 14 while(scanf("%d",&n)!=EOF&&n){ 15 sum[0]=0; 16 memset(pos,0,sizeof(pos)); 17 for(int i=1;i<=n;i++){ 18 scanf("%d",&a[i]); 19 sum[i]=sum[i-1]+a[i]; 20 } 21 for(int i=1;i<=n;i++) 22 scanf("%d",&val[i]); 23 int i=1,j=n; 24 while(i<j){ 25 long long sum1=sum[i]; 26 long long sum2=sum[n]-sum[j-1]; 27 if(sum1==sum2){ 28 pos[i]=j; 29 i++,j--; 30 } 31 else if(sum1<sum2)i++; 32 else j--; 33 } 34 memset(f,0,sizeof(f)); 35 /*for(int i=1;i<=n;i++){ 36 printf("%d ",pos[i]); 37 }*/ 38 f[0]=val[n]; 39 for(int i=1;i<=n;i++){ 40 if(!pos[i])continue; 41 f[i]=val[i]+val[n-pos[i]+1]; 42 for(int j=0;j<i;j++){ 43 if(pos[j]!=0){ 44 f[i]=min(f[i],f[j]+val[i-j]+val[pos[j]-pos[i]]); 45 } 46 } 47 } 48 int ans=val[n]; 49 50 for(int i=0;i<=n;i++) 51 if(pos[i]||i==0){ 52 if(pos[i]>i+1) 53 ans=min(ans,f[i]+val[pos[i]-i-1]); 54 else 55 ans=min(ans,f[i]); 56 } 57 printf("%d\n",ans); 58 59 /* for(int i=0;i<=n;i++) 60 printf("%d %d %d\n",i,pos[i],f[i]);*/ 61 } 62 return 0; 63 }
以上是关于HDU4960Another OCD Patient的主要内容,如果未能解决你的问题,请参考以下文章
HDU 2514 Another Eight Puzzle(DFS)