O - Treats for the Cows
入门题: O - Treats for the Cows题意:在一个长度为n的区间里,你可以在两端(左端或者右端)取出一个数,这个数乘以他是第几次取出来的。求和的最大值。
思路:一眼贪心模拟简单题。后来发现不行一旦一个数字很大就没法贪心了。给个数据
5
90
80
1
2
100
所以就想到d一下。不能常规d这样无法确定最终状态。看了下题解,顺便学习一下区间dp,具体看题解。
题解:dp[ i ][ j ]:表示在区间[ i , j ](j>=i) 里可以获得的最大值。
如果我们从里往外进行计算,找一个数以他作为最后取出来的,由题意可以知道区间[ i , j ]只可能由区间 a[ i ] + [ i+1,j ]或者 [ i , j-1 ] +a[ j ]组成,结合题意
那么状态转移是dp[ i ][ j ] = max ( dp[ i ][ j - 1 ] + a[ j ] * ( n - ( j - i ) , dp[ i + 1 ][ j ] + a[ i ] * ( n - ( j - i ) ) );
这里i要倒序否则影像记录状态(看转移方程)输入用1-n记录j-1越界麻烦。
题解参考(转载)
Brackets sequence UVA - 1626
括号匹配问题: 输入输出太毒瘤了。**题意:**一组只由[ ,],(, ),组成的字符串,然后对他进行增添字符,使其匹配;求给他增添的最少的括号的数量,然后输出匹配后的序列;
看了一个括号匹配学习了一下自己写没过 用那个学习的源代码也没过(那个不针对题目只针对这类问题)一脸懵逼。后来发现各种输入输出毒瘤,改了好一会终于过啦。
输入:数字后面有空格,每个字符串前面有空行。一整行读入字符串。
输出:每个输出之间有空行。最后一行后面没有空行。
题解: dp[i][j]表示区间内需要匹配得数。
如果两端相同dp[i][j]=dp[i+1][j-1]没啥好解释的。
如果两端不同分为两段去找最小的数在i-j找k试得数最小
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
还挺有意思为什么分为两段,那是因为dp本质就是枚举二分记忆化(不懂建议回到本文开始点击开始dp之路的链接。)
#include <bits/stdc++.h>
#define mem(s,ch) memset(s,ch,sizeof(s))
typedef long long LL;
#define inf 0x3f3f3f3f
const long long N=1000000;
const long long mod=1e9+7;
#define endl '\n'
using namespace std;
bool check(char a,char b){
if((a=='('&&b==')')||(a=='['&&b==']')) return true;
else return false;
}
int dp[110][110];
char q[110];
void print(int l,int r){
if(l>r) return;
if(l==r){
if(q[l]=='('||q[l]==')') cout<<"()";
else if(q[l]=='['||q[l]==']') cout<<"[]";
return ;
}
if(check(q[l],q[r])&&dp[l][r]==dp[l+1][r-1]){
cout<<q[l];
print(l+1,r-1);
cout<<q[r];
return ;
}
for(int k=l;k<r;k++){
if(dp[l][r]==dp[l][k]+dp[k+1][r]){
print(l,k);
print(k+1,r);
return ;
}
}
}
int main(){
int c;
cin>>c;getchar();
for(int ii = 1; ii <= c; ii ++)
{
cin.getline(q,110);
cin.getline(q,110);
//cin>>q;
int len=strlen(q);
memset(dp,0,sizeof(dp));
for(int i=0;i<len;i++) dp[i][i]=1;
for(int i=len-2;i>=0;i--){
for(int j=i+1;j<len;j++){
dp[i][j]=inf;
if(check(q[i],q[j])) dp[i][j]=dp[i+1][j-1];
for(int k=i;k<j;k++){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
}
// cout<<dp[0][len-1]<<endl;
print(0,len-1);
cout<<endl;
if(ii < c)
cout<<endl;
}
return 0;
}
同类型题(没有这题读入如此奇葩都很简单):Brackets
Brackets Sequence
Cheapest Palindrome POJ - 3280s
题目链接: Cheapest Palindrome POJ - 3280s你需要懂得前置技能点:如何是得一个字符串添加最少的字母使其为回文串:
1.得字符串逆串求两个最长公共序列
2.区间dp
这题题意:一个由m种字母组成的长度为n的串,有各字母添加和删除花费的代价,求为回文最小代价。
题解链接:POJ-3280 Cheapest Palindrome—区间DP
代码还没完全写完,看题解链接。