【HDU4960】Another 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     }
View Code

  然后我去网上查了一下,发现这个思想用记忆搜索可以过。。。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 }
View Code
 

 然后又开始想迭代该怎么写···

 先按照上面的方法预处理出一个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 }
View Code

猜你喜欢

转载自www.cnblogs.com/LQLlulu/p/8986612.html
今日推荐