Wikioi 1048 石子归并
题目描述 Description
有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。
输入描述 Input Description
第一行一个整数n(n<=100)
第二行n个整数w1,w2...wn (wi <= 100)
输出描述 Output Description
一个整数表示最小合并代价
样例输入 Sample Input
4
4 1 1 4
样例输出 Sample Output
18
思路:
以区间长度划分阶段
ps.如果是任意两堆直接贪心,越是先合并的计算次数就越多
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 7 using namespace std; 8 const int maxn = 200,maxnum = 10000000; 9 int n,j,value[maxn],sum[maxn],dp[maxn][maxn]; 10 int main(){ 11 cin>>n; 12 for(int i = 1;i <= n;i++){ 13 cin>>value[i]; 14 sum[i]= sum[i-1] + value[i]; 15 } 16 for(int l = 2;l <= n;l++){ 17 for(int i = 1;i <= n - l + 1;i++){ 18 j = i + l - 1; 19 dp[i][j] = maxnum; 20 for(int k = i;k < j;k++) 21 dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]); 22 } 23 } 24 cout<<dp[1][n]; 25 return 0; 26 }
Wikioi 3002 石子归并3
题目描述 Description
有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。
输入描述 Input Description
第一行一个整数n(n<=3000)
第二行n个整数w1,w2...wn (wi <= 3000)
输出描述 Output Description
一个整数表示最小合并代价
样例输入 Sample Input
4
4 1 1 4
样例输出 Sample Output
18
数据范围及提示 Data Size & Hint
数据范围相比“石子归并” 扩大了
思路:
四边形不等式,记录区间划分点K
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 7 using namespace std; 8 const int maxn = 4000,maxnum = 100000000; 9 int n,j,value[maxn],sum[maxn],dp[maxn][maxn],a[maxn][maxn]; 10 int main(){ 11 cin>>n; 12 for(int i = 1;i <= n;i++){ 13 cin>>value[i]; 14 sum[i]= sum[i-1] + value[i]; 15 a[i][i] = i; 16 } 17 for(int l = 2;l <= n;l++){ 18 for(int i = 1;i <= n - l + 1;i++){ 19 j = i + l - 1; 20 dp[i][j] = maxnum; 21 for(int k = a[i][j-1];k <= a[i+1][j];k++){ 22 if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]){ 23 dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]; 24 a[i][j] = k; 25 } 26 27 } 28 29 } 30 } 31 cout<<dp[1][n]; 32 return 0; 33 }
Wikioi 2102 石子归并2
题目描述 Description
在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入描述 Input Description
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出描述 Output Description
输出共2行,第1行为最小得分,第2行为最大得分.
样例输入 Sample Input
4
4 4 5 9
样例输出 Sample Output
43
54
数据范围及提示 Data Size & Hint
经典的区间动态规划。
思路:
求最小值的过程与求最大值的过程基本一样
Wikioi 3657 括号序列
题目描述 Description
我们用以下规则定义一个合法的括号序列:
(1)空序列是合法的
(2)假如S是一个合法的序列,则 (S) 和[S]都是合法的
(3)假如A 和 B 都是合法的,那么AB和BA也是合法的
例如以下是合法的括号序列:
(), [], (()), ([]), ()[], ()[()]
以下是不合法括号序列的:
(, [, ], )(, ([]), ([()
现在给定一些由‘(‘, ‘)‘, ‘[‘, ,‘]‘构成的序列 ,请添加尽量少的括号,得到一个合法的括号序列。
输入描述 Input Description
输入包括号序列S。含最多100个字符(四种字符: ‘(‘, ‘)‘, ‘[‘ and ‘]‘) ,都放在一行,中间没有其他多余字符。
输出描述 Output Description
使括号序列S成为合法序列需要添加最少的括号数量。
样例输入 Sample Input
([()
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
【样例说明】 最少添加2个括号可以得到合法的序列:()[()]或([()]) 【数据范围】 S的长度<=100 (最多100个字符)。
思路:
长度划分,状态左右端点,dp[i][j] = dp[i+1][j-1] + 1(if i matches to j)
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 7 using namespace std; 8 const int maxn = 200,maxnum = 20000000; 9 int n,c,value[maxn],dp[maxn][maxn],sign,j,q; 10 char cmd; 11 string s; 12 int main(){ 13 n = c = 0; 14 getline(cin,s); 15 q = s.length(); 16 for(int i = 1;i <= q;i++){ 17 cmd = s[i-1]; 18 if(cmd == ‘(‘) {value[i-c] = 1;n++;} 19 if(cmd == ‘[‘) {value[i-c] = 2;n++;} 20 if(cmd == ‘]‘) {value[i-c] = 3;n++;} 21 if(cmd == ‘)‘) {value[i-c] = 4;n++;} 22 } 23 memset(dp,0,sizeof(dp)); 24 for(int i = 1;i <= n;i++) dp[i][i] = 1; 25 for(int l = 2;l <= n;l++){ 26 for(int i = 1;i <= n -l + 1;i++){ 27 j = i + l - 1; 28 dp[i][j] = maxnum; 29 if(value[i] + value[j] == 5 && value[i] < value[j]) dp[i][j] = dp[i+1][j-1]; 30 for(int k = i;k < j;k++){ 31 dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j]); 32 } 33 34 } 35 } 36 cout<<dp[1][n]; 37 return 0; 38 }