hdu 4960 Another OCD Patient (最短路 解法

http://acm.hdu.edu.cn/showproblem.php?pid=4960

2014 Multi-University Training Contest 9

Another OCD Patient

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 181    Accepted Submission(s): 58


Problem Description
Xiaoji is an OCD (obsessive-compulsive disorder) patient. This morning, his children played with plasticene. They broke the plasticene into N pieces, and put them in a line. Each piece has a volume Vi. Since Xiaoji is an OCD patient, he can't stand with the disorder of the volume of the N pieces of plasticene. Now he wants to merge some successive pieces so that the volume in line is symmetrical! For example, (10, 20, 20, 10), (4,1,4) and (2) are symmetrical but (3,1,2), (3, 1, 1) and (1, 2, 1, 2) are not.

However, because Xiaoji's OCD is more and more serious, now he has a strange opinion that merging i successive pieces into one will cost ai. And he wants to achieve his goal with minimum cost. Can you help him?

By the way, if one piece is merged by Xiaoji, he would not use it to merge again. Don't ask why. You should know Xiaoji has an OCD.
 

Input
The input contains multiple test cases.

The first line of each case is an integer N (0 < N <= 5000), indicating the number of pieces in a line. The second line contains N integers Vi, volume of each piece (0 < Vi <=10^9). The third line contains N integers ai (0 < ai <=10000), and a1 is always 0.

The input is terminated by N = 0.
 

Output
Output one line containing the minimum cost of all operations Xiaoji needs.
 

Sample Input
5 6 2 8 7 1 0 5 2 10 20 0
 

Sample Output
10
Hint
In the sample, there is two ways to achieve Xiaoji's goal. [6 2 8 7 1] -> [8 8 7 1] -> [8 8 8] will cost 5 + 5 = 10. [6 2 8 7 1] -> [24] will cost 20.
 

Source
 

Recommend
hujie   |   We have carefully selected several similar problems for you:  4970 4969 4968 4967 4966

题意:给出一个数列,包含n个数字v[i],再给出一个代价表a[j]。可以将v[]中相邻的j个数字合并(相加)为一个数,代价为a[j],每个v[i]最多被合并一次(新生成的数字不能被合并)。求 将v[]合并为一个回文串 所需的最小代价。

题解:

必须要把v[]合成回文串,所以最左边的若干个数合成,要等于最右边的若干个数合成。我们可以设两个下标l和r,表示左右各包括哪里了,若左边的和小于右边的和,l++,否则r--,直到左边的和等于右边的和,这时左右就分出了一组和相同的组合,我们只需要分别记录两边的元素个数。然后对[l+1,r-1]这个中间的区间,继续这个操作。最后中间可能会剩下一些数,将其元素个数记为mid。

然后我们就得到了很多组个数,用这些就可以算出答案。因为可能的合成方案就是我们这些组各合各的,最后就能组成一个回文串;或者其中相邻的若干组合成,因为各组本来就是对称相等的,相邻的组合成也是相等的;还有就是中间的mid和靠近中间的若干组合成。

这个最优结果怎么求呢?我想到了最短路。建个图,把合成操作当做路加入图中,i->j的路表示把第i+1,i+2,...j组合成为一组,路的长度为代价(可由各组的元素个数和求得,个数和可以先预处理,以便快速求得)。i->i+1代表这一组单独用,不合成,路的长度为代价。再弄一点把中间那块mid和其他的合成的路。

然后d[i]为0到i的最短路,也就是处理完第i组的最小消耗,d[处理完中间那块]就是答案。

不过要600ms,看来还是不如标准做法,我怕了,日后再学标准的。

代码:

  1 //#pragma comment(linker, "/STACK:102400000,102400000")
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<iostream>
  5 #include<cstring>
  6 #include<algorithm>
  7 #include<cmath>
  8 #include<map>
  9 #include<set>
 10 #include<stack>
 11 #include<queue>
 12 using namespace std;
 13 #define ll long long
 14 #define usll unsigned ll
 15 #define mz(array) memset(array, 0, sizeof(array))
 16 #define minf(array) memset(array, 0x3f, sizeof(array))
 17 #define REP(i,n) for(i=0;i<(n);i++)
 18 #define FOR(i,x,n) for(i=(x);i<=(n);i++)
 19 #define RD(x) scanf("%d",&x)
 20 #define RD2(x,y) scanf("%d%d",&x,&y)
 21 #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)
 22 #define WN(x) prllf("%d\n",x);
 23 #define RE  freopen("D.in","r",stdin)
 24 #define WE  freopen("1biao.out","w",stdout)
 25 #define mp make_pair
 26 #define pb push_back
 27 
 28 const int maxn=5555;
 29 const int INF=2*(1e9);
 30 ll v[maxn];
 31 int a[maxn];
 32 int n;
 33 //vector<int>SL,SR;
 34 //int mid;
 35 
 36 
 37 const int maxm=maxn*maxn/2;
 38 struct edge {
 39     int v,next;
 40     int w;
 41 } e[maxm];
 42 int head[maxn],en;
 43 
 44 inline void add(const int &x,const int &y,const int &z) {
 45     e[en].w=z;
 46     e[en].v=y;
 47     e[en].next=head[x];
 48     head[x]=en++;
 49 }
 50 
 51 bool f[maxn];///入队标志
 52 int b[maxn];
 53 int d[maxn];///b为循环队列,d为起点到各点的最短路长度
 54 void spfa(int n) { ///0~n-1 共n个点
 55     int i,k;
 56     int st=0, l=0, r=1;
 57 
 58     memset(f,0,sizeof(f));
 59     memset(b,0,sizeof(b));
 60     for(i=0; i<n; i++)
 61         d[i]=INF;
 62     b[0]=st;
 63     f[st]=1;
 64     d[st]=0;
 65     while(l!=r) {
 66         k=b[l++];
 67         l%=n;
 68         for(i=head[k]; i!=-1; i=e[i].next)
 69             if (d[k]+e[i].w < d[e[i].v]) {
 70                 d[e[i].v]=d[k] + e[i].w;
 71                 if (!f[e[i].v]) {
 72                     if(d[e[i].v]>d[b[l]]) {///SLF优化
 73                         b[r++]=e[i].v;
 74                         r%=n;
 75                     } else {
 76                         l--;
 77                         if(l==-1)l=n-1;
 78                         b[l]=e[i].v;
 79                     }
 80                     f[e[i].v]=1;
 81                 }
 82             }
 83         f[k]=0;
 84     }
 85 }
 86 
 87 void init() {
 88     memset(head,-1,sizeof(head));
 89     en=0;
 90 }
 91 
 92 
 93 int suml[maxn],sumr[maxn];
 94 int ql[maxn],qr[maxn],qn;
 95 int farm() {
 96     if(n==1)return 0;
 97     int i,j,k;
 98 
 99     qn=0;
100     int mid=0;
101     int l=0,r=n-1;
102     int L=l,R=r;
103     ll sl=v[l],sr=v[r];
104     while(l<r) {
105         while(sl<sr) l++,sl+=v[l];
106         while(sl>sr) r--,sr+=v[r];
107         if(sl==sr && l<r) {
108             ql[qn]=l-L+1;
109             qr[qn]=R-r+1;
110             qn++;
111             l++;r--;
112             L=l;R=r;
113             sl=v[l];sr=v[r];
114         }
115     }
116     mid=R-L+1;
117 //    REP(i,qn)printf("%3d",ql[i]);
118 //    puts("");
119 //    REP(i,qn)printf("%3d",qr[i]);
120 //    printf("   mid=%d\n",mid);
121     mz(suml);
122     mz(sumr);
123     for(i=1; i<=qn; i++) {
124         suml[i]=suml[i-1]+ql[i-1];
125         sumr[i]=sumr[i-1]+qr[i-1];
126     }
127     init();
128     for(i=0; i<qn; i++)
129         for(j=i+1; j<=qn; j++) {
130             add(i,j, a[ suml[j] - suml[i] ] + a[ sumr[j]-sumr[i] ] );
131         }
132     for(i=0; i<=qn; i++) {
133         add(i, qn+1, a[ suml[qn] - suml[i] + sumr[qn] - sumr[i] + mid ]);
134     }
135     spfa(qn+2);///0为起点,d[i]为搞完第i块的最少消耗
136     return d[qn+1];
137 }
138 
139 int main() {
140     int i;
141     while(scanf("%d",&n)!=EOF) {
142         if(n==0)break;
143         REP(i,n)scanf("%I64d",&v[i]);
144         FOR(i,1,n)scanf("%d",&a[i]);
145         printf("%d\n",farm());
146     }
147     return 0;
148 }
View Code

 

posted @ 2014-08-19 21:10  带鱼Yuiffy  阅读(300)  评论(0编辑  收藏  举报