-
A -- 石子归并
- Description
现在有n堆石子,第i堆有ai个石子。现在要把这些石子合并成一堆,每次只能合并相邻两个,每次合并的代价是两堆石子的总石子数。求合并所有石子的最小代价
- Input
第一行包含一个整数T(T<=50),表示数据组数
每组数据第一行包含一个整数n(2<=n<=100),表示石子的堆数
第二行包含n个正整数ai(ai<=100),表示每堆石子的石子数
- Output
每组数据仅一行,表示最小合并代价
- Sample Input
2 4 1 2 3 4 5 3 5 2 1 4
- Sample Output
19 33
- 题目理解
这道题和之前的贪心合并果子很像,但是这里有一个限制条件,每次只能合并相邻的两堆果子,就不能保证贪心的思想能够实现,就算模拟实现也比较麻烦,这里想到区间DP是因为对于长度到的果子最后一定是两堆果子合并而且为两段连续区间,就可以枚举断点,大区间由小区间推出来,表示合并区间的代价,因为合并两区间的代价值是总石子数,由于是连续区间,预处理一下以结尾的总石子数,所以得出
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
int dp[maxn][maxn],a[maxn];
int main(){
int t,n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
a[i]=a[i]+a[i-1];
}
for(int j=2;j<=n;++j){
for(int i=j-1;i>=1;--i){
int ans=1e9;
for(int k=i;k<j;k++){
ans=min(ans,dp[i][k]+dp[k+1][j]+a[j]-a[i-1]);
}
dp[i][j]=ans;
}
}
printf("%d\n",dp[1][n]);
}
return 0;
}
-
B -- Anniversary party
- Description
There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure of employees. It means that the supervisor relation forms a tree rooted at the rector V. E. Tretyakov. In order to make the party funny for every one, the rector does not want both an employee and his or her immediate supervisor to be present. The personnel office has evaluated conviviality of each employee, so everyone has some number (rating) attached to him or her. Your task is to make a list of guests with the maximal possible sum of guests' conviviality ratings
- Input
Employees are numbered from 1 to N. A first line of input contains a number N. 1 <= N <= 6 000. Each of the subsequent N lines contains the conviviality rating of the corresponding employee. Conviviality rating is an integer number in a range from -128 to 127. After that go T lines that describe a supervisor relation tree. Each line of the tree specification has the form:
L K
It means that the K-th employee is an immediate supervisor of the L-th employee. Input is ended with the line
0 0
- Output
Output should contain the maximal sum of guests' ratings
- Sample Input
7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5 0 0
- Sample Output
5
- 题目理解
由于题目也没有明说根节点的表示,但对于一个结点一定会有存在或者不存在的情况,而与它相连的结点(父亲和儿子相同地位)都不能出现,由此我们可以直接建图,遍历的时候使用vis数组表示前面遍历的结点将图转变为有根树。转移过程对于结点来说它可以存在或者不存在,使用和来表示,如果它存在则其儿子都不存在;它不存在的时候儿子可以存在可以不存在取最大值,初始化为阶层的数量,其他为0
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=6005;
vector<int>son[maxn];
int dp[maxn][2],vis[maxn];
int n;
void dfs(int root)
{
vis[root]=1;
for(int i=0;i<son[root].size();i++)
{
int v=son[root][i];
if(!vis[v])
{
dfs(v);
dp[root][1]+=dp[v][0];
dp[root][0]+=max(dp[v][0],dp[v][1]);
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<=n;i++)
son[i].clear();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
scanf("%d",&dp[i][1]);
dp[i][0]=0;
}
int f,s;
while(scanf("%d%d",&s,&f)!=EOF)
{
if(s==0&&f==0)
break;
son[f].push_back(s);
son[s].push_back(f);
}
dfs(1);
printf("%d\n",max(dp[1][0],dp[1][1]));
}
return 0;
}
-
C -- Multiplication Puzzle
- Description
The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row.
The goal is to take cards in such order as to minimize the total number of scored points.
For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring
10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000
If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be
1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150
- Input
The first line of the input contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces
- Output
Output must contain a single integer - the minimal score
- Sample Input
6 10 1 50 50 20 5
- Sample Output
3650
- 题目理解
对于一段序列的相同操作求最小最大的情况使用区间DP就要适当的选取断点,我们知道最后一段区间一定会剩下首和尾,如果两段区间的首和尾为同一个数,那么这个中间值的消除就完成了长区间的消除所有元素,我们这时候思考如何由短区间表示,如上述分析我们得出
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
int dp[maxn][maxn],a[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for(int j=3;j<=n;++j){
for(int i=j-1;i>=1;--i){
for(int k=i+1;k<j;++k){
if(dp[i][j]==0) dp[i][j]=dp[i][k]+dp[k][j]+a[i]*a[k]*a[j];
else dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);
//printf("%d %d %d\n",i,k,j);
//printf("!!%d %d %d\n",a[i],a[k],a[j]);
//printf("%d %d %d\n",dp[i][k],dp[k][j],dp[i][j]);
}
}
}
printf("%d\n",dp[1][n]);
}
return 0;
}
-
D -- Zuma
- Description
Genos recently installed the game Zuma on his phone. In Zuma there exists a line of n gemstones, the i-th of which has color ci. The goal of the game is to destroy all the gemstones in the line as quickly as possible.
In one second, Genos is able to choose exactly one continuous substring of colored gemstones that is a palindrome and remove it from the line. After the substring is removed, the remaining gemstones shift to form a solid line again. What is the minimum number of seconds needed to destroy the entire line?
Let us remind, that the string (or substring) is called palindrome, if it reads same backwards or forward. In our case this means the color of the first gemstone is equal to the color of the last one, the color of the second gemstone is equal to the color of the next to last and so on
- Input
The first line of input contains a single integer n (1 ≤ n ≤ 500) — the number of gemstones
The second line contains n space-separated integers, the i-th of which is ci (1 ≤ ci ≤ n) — the color of the i-th gemstone in a line
- Output
Print a single integer — the minimum number of seconds needed to destroy the entire line
- Sample Input
7 1 4 4 2 3 2 1
- Sample Output
2
- 题目理解
对于区间dp我们能够枚举断点来模拟所有情况,还要解决一个问题,如何在代码中表达回文问题。 如果一个区间来说如果两端是相同的数字,那么它们的消除不会累计步骤,因为可以和小区间里任意一个一起消除,如果长度为2的区间就需要单独赋值为1。对大区间来说,如果其本身就是回文区间是最优,会由最小区间不断扩增扩增,但是区间值保持1不变,因为前面说的不累计步骤;如果需要切割成小区间,如果它能分成两个小的回文区间自然是次优,原理同上,如果都不行那么任意断点取所有可能最优值
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[505],dp[505][505];
int main()
{
int n;
scanf("%d",&n);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
dp[i][i]=1;
}
for(int j=2;j<=n;++j){
for(int i=j-1;i>=1;--i){
if(a[i]==a[j]){
//printf("!!%d %d\n",i,j);
if(i+1<=j-1){
if(dp[i][j]) dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
else dp[i][j]=dp[i+1][j-1];
}
else dp[i][j]=1;
}
for(int k=i;k<j;++k){
if(dp[i][j]) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
else dp[i][j]=dp[i][k]+dp[k+1][j];
}
//printf("%d %d : %d\n",i,j,dp[i][j]);
}
}
printf("%d\n",dp[1][n]);
return 0;
}
-
I -- Post Office
- Description
There is a straight highway with villages alongside the highway. The highway is represented as an integer axis, and the position of each village is identified with a single integer coordinate. There are no two villages in the same position. The distance between two positions is the absolute value of the difference of their integer coordinates
Post offices will be built in some, but not necessarily all of the villages. A village and the post office in it have the same position. For building the post offices, their positions should be chosen so that the total sum of all distances between each village and its nearest post office is minimum
You are to write a program which, given the positions of the villages and the number of post offices, computes the least possible sum of all distances between each village and its nearest post office
- Input
Your program is to read from standard input. The first line contains two integers: the first is the number of villages V, 1 <= V <= 300, and the second is the number of post offices P, 1 <= P <= 30, P <= V. The second line contains V integers in increasing order. These V integers are the positions of the villages. For each position X it holds that 1 <= X <= 10000
- Output
The first line contains one integer S, which is the sum of all distances between each village and its nearest post office
- Sample Input
10 5 1 2 3 6 7 9 11 22 44 50
- Sample Output
9
- 题目理解
前个村子建立个邮局,如果那么最近距离和最小值为0,因为可以在每个村子建一个及以上。对于的情况,我们首先考虑如果是区间DP那么可以对以及进行断点枚举,拆分为子问题的时候其表现形式应该与前面保持一致。定义为前个村子建立个邮局,那么当大区间拆分成两个小区间的时候我们可以用来表示,而对于后面的值无法表示。通过累积的思想如果我们在前面个村落建立个邮局,剩下后面的村落建立一个邮局,并且从中位数的原理连续几个村落建立一个邮局的最优值 容易得出,从而得出转移方程其中
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=305;
const int maxm=35;
int cot[maxn][maxn],dp[maxn][maxm],a[maxn];
int main()
{
int v,p;
while(scanf("%d%d",&v,&p)!=EOF){
for(int i=1;i<=v;++i)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));//表示没有计算值
memset(cot,0,sizeof(cot));
for(int i=1;i<v;++i){
for(int j=i+1;j<=v;++j){
cot[i][j]=0;
int mid=(i+j)/2;//当为整数的时候建在左边和建在右边的最小值相同
for(int k=i;k<=j;++k)
cot[i][j]+=((a[k]-a[mid])>0?(a[k]-a[mid]):(a[mid]-a[k]));
}
}
for(int j=2;j<=p;++j){
for(int i=j+1;i<=v;++i){
for(int k=1;k<i;++k){
if(j==2){
if(dp[i][j])
dp[i][j]=min(dp[i][j],cot[1][k]+cot[k+1][i]);
else
dp[i][j]=cot[1][k]+cot[k+1][i];
//printf("###%d %d\n",cot[1][k],cot[k+1][i]);
}
else{
if(dp[i][j])
dp[i][j]=min(dp[i][j],dp[k][j-1]+cot[k+1][i]);
else
dp[i][j]=dp[k][j-1]+cot[k+1][i];
}
//printf("%d %d %d: %d\n",j,k,i,dp[i][j]);
}
//printf("%d\n",dp[i][j]);
}
}
if(p==1)
printf("%d\n",cot[1][v]);
else
printf("%d\n",dp[v][p]);
}
return 0;
}