程序设计思维week12 选做题

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值