括号序列的dp问题模型
Codeforces314E
◦给定一个长度为n的仅包含左括号和问号的字符串,将问号变成左括号或
右括号使得该括号序列合法,求方案总数。
◦例如(())与()()都是合法的括号序列。
◦ n<=3000。
在括号序列问题中,总是把左括号看作+1,右括号看作-1,要使括号序列合法,只需满足任意一个前缀和大于
或等于0,且总和为0即可
设dp[i][j]表示第i个字符,还剩下j个左括号的方案数
若第i+1个字符是左括号,则dp[i+1][j+1]+=dp[i][j]
若第i+1个字符是右括号,则dp[i+1][j-1]+=dp[i][j]
若第i+1个字符是问号,则dp[i+1][j+1]+=dp[i][j],dp[i+1][j-1]+=dp[i][j]
答案为:dp[n][0]
BZOJ3709
◦在一款电脑游戏中,你需要打败n只怪物(从1到n编号)。为了打败第i只
怪物,你需要消耗d[i]点生命值,但怪物死后会掉落血药,使你恢复a[i]点
生命值。任何时候你的生命值都不能降到0(或0以下)。
◦请问是否存在一种打怪顺序,使得你可以打完这n只怪物而不死掉。
◦N<=10^5
巧妙的贪心题目(之所以引入这道贪心,是因为这道题的解题思路对下一题有帮助)
引入zhhx的一句话:
贪心:noip的贪心很多都是按照某种方式排序,然后依次选或处理。
显然,当a[i]>d[i]时,打第i只怪物会回血,所以先打能回血的怪物
在能回血的怪物里,我们需要先打d[i]小的怪物,故按照d[i]从小到大排序
那么对于不能回血的怪物,我们该怎样排序呢?
首先,如果我们最后能活下来,那么剩余的生命值一定是固定的,所以反着来考虑,和正着是一样的
反着来想我们需要先打a[i]小的怪物,正着来想就是按照a[i]从大到小排序
因此我们把这些怪物分成两组,a[i]-d[i]>0的为一组,a[i]-d[i]<0的为一组
再分别按照d[i]从小到大,a[i]从大到小进行排序
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define Maxn 100100
#define ll long long
int n;
ll z;
struct node{
int dec,inc,num;
}a[Maxn],b[Maxn];
bool cmp1(node x,node y)
{
return x.dec<y.dec;
}
bool cmp2(node x,node y)
{
return x.inc>y.inc;
}
int ans[Maxn];
int tot1=0,tot2=0;
int main()
{
scanf("%d%lld",&n,&z);
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(y-x>=0)//
{
a[++tot1].dec=x;a[tot1].inc=y;a[tot1].num=i;
}
else
{
b[++tot2].dec=x;b[tot2].inc=y;b[tot2].num=i;
}
}
sort(a+1,a+tot1+1,cmp1);
sort(b+1,b+tot2+1,cmp2);
for(int i=1;i<=tot1;i++)
{
z-=a[i].dec;
if(z<=0){printf("NIE");return 0;}
z+=a[i].inc;
}
for(int i=1;i<=tot2;i++)
{
z-=b[i].dec;
if(z<=0){printf("NIE");return 0;}
z+=b[i].inc;
}
printf("TAK\n");
for(int i=1;i<=tot1;i++)printf("%d ",a[i].num);
for(int i=1;i<=tot2;i++)printf("%d ",b[i].num);
return 0;
}