D
题目描述
样例
Sample Input
((()))
()()()
([]])
)[)(
([][][)
end
Sample Output
6
6
4
0
6
思路分析
区间DP,找到最长的合法子序列的长度。
数组dp[i][j]表示区间[i,j]中最长合法子序列的长度,初始化为0。
除空序列外,还有两种合法构造。
-
若S合法,则(S)和[S]合法,即左右两端为()或[],合法长度加2。dp[i][j]=dp[i+1][j-1]+2;
-
若A,B合法,则AB合法。
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]);
嵌套for循环,从长度为2,i=0的子序列开始,将i不断增大(向尾部移动),长度增长,更新当前子序列的最长合法长度。
最终答案为dp[0][str.size()-1]。
AC代码
#include<iostream>
#include<string.h>
#include<algorithm>
#include<string>
using namespace std;
int dp[105][105];
int main()
{
string str;
while(cin>>str)
{
if(str=="end") return 0;
memset(dp,0,sizeof(dp));
for(int len=2;len<=str.size();len++)
{
for(int i=0;i<str.size();i++)
{
int j=i+len-1;
if(j>=str.size()) break;
if(str[i]=='('&&str[j]==')'||str[i]=='['&&str[j]==']')
{
dp[i][j]=dp[i+1][j-1]+2;
}
int k=i;
while(k<j)
{
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]);
k++;
}
}
}
cout<<dp[0][str.size()-1]<<endl;
}
return 0;
}
E
题目描述
马上假期就要结束了,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
在第二个样例中,按照 Computer->English->Math 和 Computer->Math->English 的顺序完成作业,所扣的分数都是 3,由于 English 的字典序比 Math 小,故输出前一种方案。
思路分析
状压DP+位运算
结构体HW存放任务名称name,截至时间d和需要天数c。
dp[S]表示完成S作业集合后被扣的最少分数。
转移方程:dp[S|(1<<x)]= dp[S]+作业x被扣的分数temp
temp的计算方法:
sum=S作业集合对应总时间
temp=max((sum+tasks[x].c-tasks[x].d),0);
用数组pre[S]记录每个状态对应的最后一个作业,在输出时从后向前递归的输出。
AC代码
#include<iostream>
#include<string>
#include<string.h>
#include<cmath>
using namespace std;
const int Max=1e9;
struct HW
{
string name;
int d,c;
};
HW tasks[20];
int dp[50000],pre[50000],sum[50000];
void output(int x)
{
if(x==0) return;
output(x-(1<<pre[x]));
cout<<tasks[pre[x]].name<<endl;
}
int main()
{
int num;
cin>>num;
while(num--)
{
int n;
cin>>n;
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
memset(sum,0,sizeof(sum));
for(int i=0;i<n;i++)
{
cin>>tasks[i].name>>tasks[i].d>>tasks[i].c;
}
for(int i=1;i<=(1<<n);i++)
{
dp[i]=Max;
for(int j=n-1;j>=0;j--)
{
if(i&(1<<j))
{
int k=i-(1<<j);
int temp=max((sum[k]+tasks[j].c-tasks[j].d),0);
if(dp[i]>dp[k]+temp)
{
dp[i]=dp[k]+temp;
pre[i]=j;
sum[i]=sum[k]+tasks[j].c;
}
}
}
}
cout<<dp[(1<<n)-1]<<endl;
output((1<<n)-1);
}
return 0;
}