洛谷 P1880 石子合并 破环成链

P1880 石子合并

  • 时空限制1s / 128MB

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

 

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

 

输出格式:

 

输出共2行,第1行为最小得分,第2行为最大得分.

 

输入输出样例

输入样例#1:  
4
4 5 9 4
输出样例#1: 
43
54
---------------------------------------------------------------------------------
这道题的难点就是如何在一个环上实现DP
环不好搞,那就把它变成一条链吧!


其实就是就是破环成链

做法也挺简单,就是把原来的数列扩展为原来的两倍
然后把长度为n的数列从这个2n的数列的第一个数开始整体右移
一共移n次,即在这个新数列上进行n次循环,找每次移动操作得出的最大或最小值
需要注意每次移动后要清空dp状态值,因为移动后就相当于重新操作,状态值是原先转移前序列的状态值

dp[i][j]表示合并第i个数到第j个数可获得的最大值,dpmi[i][j]则为最小值
sum[i][j]表示第i个数到第j个数的和
注意dpmi[i][i]==0,因为只有一个数时还没合并,所以该状态没有分数

AC代码:
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define maxn 233
 5 using namespace std;
 6 int n,dp[maxn][maxn],a[maxn],ansma,ansmi,sum[maxn][maxn],dpmi[maxn][maxn];
 7 int main(){
 8     scanf("%d",&n);
 9     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
10     
11     for(int i=n+1;i<=n*2;i++) a[i]=a[i-n];
12     for(int i=1;i<=maxn;i++)
13        for(int j=1;j<=maxn;j++)
14        dpmi[i][j]=1e9;
15     ansmi=1e9;
16     for(int i=1;i<=n*2;i++)
17        for (int j=i;j<=n*2;j++) 
18        sum[i][j]+=sum[i][j-1]+a[j];
19        
20     for(int i=1;i<=n;i++){
21         
22         for(int j=i;j<n+i-1;j++){
23             dp[j][j+1]=sum[j][j+1];
24             dpmi[j][j+1]=sum[j][j+1];
25             dpmi[j][j]=0;
26         }
27         dpmi[n+i-1][n+i-1]=0;
28         
29         for(int j=2;j<=n-1;j++)
30           for(int g=i;g+j<=n+i-1;g++)
31               for(int k=g;k<=g+j-1;k++){
32                  dp[g][g+j]=max(dp[g][g+j],dp[g][k]+dp[k+1][g+j]+sum[g][g+j]);
33                  dpmi[g][g+j]=min(dpmi[g][g+j],dpmi[g][k]+dpmi[k+1][g+j]+sum[g][g+j]);
34              }
35         
36         ansma=max(ansma,dp[i][n+i-1]);
37         ansmi=min(ansmi,dpmi[i][n+i-1]);
38         
39         for(int i=1;i<=maxn;i++)
40             for(int j=1;j<=maxn;j++)
41             dpmi[i][j]=1e9;
42         memset(dp,0,sizeof(dp));
43         
44     }
45     printf("%d\n%d",ansmi,ansma);
46     return 0;
47 }
破环成链

 -----------------------------------------------------------------------------------------------------------------------------

话说上一次以以上自己想出来的做法AC后,发现以上做法跑得比其他人的慢得多,所以又回来学习了一波

代码简化了许多,更重要的是跑得更快了

破环成链是没错

只是在原来dp过程中可进行很多优化

直接放代码:

 

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define maxn 233
 5 using namespace std;
 6 int read();
 7 int n,dpma[maxn][maxn],sum[maxn],a[maxn],mi,ma,dpmi[maxn][maxn];
 8 int main(){
 9     n=read();
10     for(int i=1;i<=n;i++){
11         a[i]=read();
12         a[i+n]=a[i];
13     }
14     for(int i=1;i<=2*n;i++) sum[i]=sum[i-1]+a[i];
15     for(int i=1;i<n;i++)//枚举区间长度 
16        for(int j=1;j<=n*2-i;j++){//枚举起点 
17              dpmi[j][j+i]=1e9;
18              for(int k=j;k<j+i;k++){//枚举中点 
19                  dpma[j][j+i]=max(dpma[j][j+i],dpma[j][k]+dpma[k+1][j+i]+sum[j+i]-sum[j-1]);
20                  dpmi[j][j+i]=min(dpmi[j][j+i],dpmi[j][k]+dpmi[k+1][j+i]+sum[j+i]-sum[j-1]);
21              }
22        }
23     mi=1e9;ma=0;
24     for(int i=1;i<n;i++){
25         ma=max(ma,dpma[i][i+n-1]);
26         mi=min(mi,dpmi[i][i+n-1]);
27     }
28     printf("%d\n%d",mi,ma);
29     return 0;
30 } 
31 int read(){
32     int ans=0,f=1;char c=getchar();
33     while('0'>c||c>'9'){if(c=='-')f=-1;c=getchar();}
34     while('0'<=c&&c<='9')ans=ans*10+c-48,c=getchar();return ans*f;
35 }
标准做法

然后就是从512ms到12ms的差距T_T

 

posted @ 2017-10-30 20:37  lpl_bys  阅读(613)  评论(0编辑  收藏  举报