1-POJ2955
问题描述
We give the following inductive definition of a “regular brackets” sequence:the empty sequence is a regular brackets sequence,if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, andif a and b are regular brackets sequences, then ab is a regular brackets sequence.no other sequence is a regular brackets sequenceFor instance, all of the following character sequences are regular brackets sequences:(), [], (()), ()[], ()[()]while the following character sequences are not:(, ], )(, ([)], ([(]Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence.Given the initial sequence ([([]])], the longest regular brackets subsequence is [([])].
Input
The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters (, ), [, and ]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.
Output
For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.
Sample Input
((()))
()()()
([]])
)[)(
([][][)
end
解题思路
一个并没有很多坑的区间dp,很适合dp入门与上手。区间dp的套路一般是通过枚举区间长度与断点来进行状态转移。这道题的大意是找到最长的合法括号子序列,转移是在位置i与j匹配成功时 f[i][j] = max(f[i][j],f[i+1][j-1] + 2);注意枚举区间断点k时维护最大值f[i][j] = max(f[i][j],f[i][k] + f[k][j]);
代码:
#include<iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define ll long long
const int maxn = 1e6+5;
int f[105][105];
string s;
ll getnum()
{
ll ans=0;
char c=getchar();
bool f=1;
while(!isdigit(c))
{
if(c=='-')f=0;
c=getchar();
}
while(isdigit(c))
{
ans=ans*10+c-'0';
c=getchar();
}
return f ? ans : -ans;
}
bool matched(int l,int r)
{
if(s[l]=='('&&s[r]==')')return true;
if(s[l]=='['&&s[r]==']')return true;
return false;
}
int main()
{
while (cin>>s)
{
if(s=="end")
{
break;
}
memset(f,0, sizeof(f));
for(int i=s.length()-1;i>=0;i--)
{
for(int j = i+1;j<s.length();j++)
{
if(matched(i,j))
{
f[i][j] = max(f[i][j],f[i+1][j-1] + 2);
}
for(int k =i;k<=j;k++)
{
f[i][j] = max(f[i][j],f[i][k] + f[k][j]);
}
}
}
cout<<f[0][s.length()-1]<<"\n";
}
return 0;
}
2-HDU1074(状压dp)
马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
Input
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数 n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100) S 表示任务的名称和两个整数 D 和 C,分别表示任务的截止时间和完成任务需要的天数。
这 n 个任务是按照字符串的字典序从小到大给出。
Output
每组测试数据,输出最少扣的分数,并输出完成作业的方案,如果有多个方案,输出字典序最小的一个。
Sample Input
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3
Sample Output
2
Computer
Math
English
3
Computer
English
Math
解题思路
状压dp可以理解为一种结合二进制优化的暴力。这道题虽然看起来n个作业并不多,但是实际会遇到的情况却是指数级的,数组开不了这么大,所以用每一个二进制位代表每个作业,如1011代表完成了作业1、2和4
枚举状态时,总是让枚举到的当前作业最后完成,计算比较消耗的时间进而更新答案。由于还要求字典序从小到大输出,一方面我们要在更新答案时记录写作业的过程(pre数组)另外注意到输入时满足字典序从小到大,那么我们从后往前遍历,就可以保证越更新答案,整个序列的字典序就越小。
#include<iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define ll long long
const int maxn = 1<<15;
const int inf = 2147483647;
struct homeworks
{
string name;
int days;
int ddl;
}a[101];
ll dp[maxn];
int pre[maxn];
ll sum[maxn];
ll getnum()
{
ll ans=0;
char c=getchar();
bool f=1;
while(!isdigit(c))
{
if(c=='-')f=0;
c=getchar();
}
while(isdigit(c))
{
ans=ans*10+c-'0';
c=getchar();
}
return f ? ans : -ans;
}
void print(int x)
{
if(x==0)return ;
print(x - (1<<pre[x]));
cout<<a[pre[x]].name<<"\n";
}
int main()
{
int T;
cin>>T;
while (T--)
{
int n;
cin>>n;
for(int i=0;i<n;i++)
z{
string name;
int days,ddl;
cin>>name>>ddl>>days;
a[i].name = name;
a[i].days = days;
a[i].ddl = ddl;
}
memset(dp,0, sizeof(dp));
memset(pre,0, sizeof(pre));
memset(sum,0, sizeof(sum));
for(int i=1;i<(1<<n);i++)
{
dp[i] = inf;
for(int j=n-1;j>=0;j--)//为了保证字典序,倒着枚举
{
if(i & (1<<j))//做过这个作业
{
int k = i - (1<<j);//还没做这个作业的情况
ll tmp = max((ll)0,sum[k] + a[j].days - a[j].ddl);
if(dp[i]>dp[k] +tmp)//计算最后做这个作业的扣分情况,记录答案
{
pre[i] = j;
sum[i] = sum[k] + a[j].days;
dp[i] = dp[k] + tmp;
}
}
}
}
cout<<dp[(1<<n)-1]<<"\n";
print((1<<n)-1);
}
return 0;
}