HUEL_ACM第二学期第六周:线性DP

A - (最长公共子序列模板) POJ - 1458

# include<iostream>
# include<algorithm>
# include<cstring>

using namespace std;

const int N = 555;

char a[N], b[N];

int main()
{
    while(cin>>a+1>>b+1)            //读入字符串a,b
    {
        int dp[N][N];                       //定义答案数组dp
        //dp[i][j]是字串a的前i个字符与字串b的前j个字符的最长公共子序列
        
        int n = strlen(a+1), m = strlen(b+1);
        
        for(int i=1; i<=n; ++i)
        {
            for(int j=1; j<=m; ++j)
            {
                dp[i][j] = max(dp[i][j-1], dp[i-1][j]);     
                //如果两个字串最后一个数不相同,就取它们之前的最长公共子序列

                if(a[i]==b[j])   dp[i][j] = max(dp[i][j], dp[i-1][j-1]+1);
                //如果最后两个字串的最后一个数相同,最长公共子序列可能为a前i-1个字符与b的前j-1个字符的最长公共子序列再+1
            }
        }
        
        cout<<dp[n][m]<<endl;           //根据定义,输出答案
    }
    
    return 0;
}

B - (思维?) HDU - 1003

主要思想:如果当前连续子序列的值为正值,那么无论它再加任何数,其总值都会大于它要加的数;如果当前连续子序列的值为负数,那么无论它再加任何数,其总值都会小于它要加的数。因此只有当当前子序列为正值的时候,该连续子序列的值才有可能增加,若为负数,则一定减小。所有当连续子序列的值为负数时,及时舍去该序列,重新再开一段序列(新序列的初始总值为0)

# include<iostream>
# include<cstring>

using namespace std;

const int N = 1e5+10;

struct Num
{
    int num, val;
}mini;

int t;
int a[N], s[N];

int main()
{
    cin>>t;
    for(int i=1; i<=t; ++i)
    {
        memset(a, 0, sizeof a);
        memset(s, 0, sizeof s);
        
        cout<<"Case "<<i<<":"<<endl;
        int n;
        cin>>n;
    
        for(int i=1; i<=n; ++i)   cin>>a[i];
        
        int l = 1, r = 1, ls = 1;
        int ans = -2e9, res = 0;
        for(int i=1; i<=n; ++i)
        {
            res += a[i];
            
            if(res > ans)
            {
                ans = res;
                r = i;
                ls = l;
            }
            
            if(res < 0)
            {
                res = 0;
                l = i+1;
            }
        }
        
        cout<<ans<<' '<<ls<<' '<<r<<endl;
        if(i != t)   cout<<endl;
    }
    
    return 0;
}

D - (最长上升子序列模板) HDU - 1087

//注意题目的数据范围,可能会爆int

# include<iostream>
# include<algorithm>

using namespace std;

const long long N = 1100;

long long n;
long long a[N];
long long dp[N];        //dp[i]是以第i个数字为结尾的最长上升子序列和

int main()
{
    while(cin>>n, n)
    {
        for(long long i=1; i<=n; ++i)   cin>>a[i];
        
        for(long long i=1; i<=n; ++i)   dp[i] = a[i];      
         //初始化,每个数都是一个子序列,每个格子的上升子序列的和都至少是自己
        
        for(long long i=1; i<=n; ++i)
        {
            for(long long j=1; j<i; ++j)
            {
                if(a[j] < a[i])   dp[i] = max(dp[i], dp[j]+a[i]);
                //如果j到i是上升子序列,那么取最值
            }
        }
        
        //根据定义的dp数组的含义,求出最值
        //注意:拥有最大和的上升子序列不一定是以原数组的最后一个数结尾,咱也不知道是以哪个数结尾,因此需遍历一遍整个数组,找出拥有最大和上升子序列
        long long ans = -2e18;
        for(long long i=1; i<=n; ++i)   ans = max(ans, dp[i]);
        cout<<ans<<endl;
    }
    
    return 0;
}

F - (最长上升子序列的优化) 计蒜客 - T2146

//对于拦截的每一个导弹,都有两种选择
//1. 使用之前的导弹拦截系统(前提是该导弹高度必须小于之前的导弹高度)
//2. 在开一个新的导弹拦截系统

# include<iostream>
# include<algorithm>
# include<cstring>

using namespace std;

const int N = 1e5+10;

int n;
int a[N];
int dp[N];
int f[N];

int main()
{
    int cnt = 1;
    while(cin>>a[cnt])  cnt++;          //  <cnt
    cnt--;
    
    //最长下降子(非上升)序列
    memset(dp, 0x3f, sizeof dp);
    for(int i=cnt; i; --i)
    {
        int x = a[i];
        int j = upper_bound(dp, dp+cnt, x)-dp;
        dp[j] = x;
    }
    int res = 0;
    while(dp[res]!=0x3f3f3f3f)   res++;
    cout<<res<<endl;
    
    
    //永远维护一个递增的序列
    //贪心:能不创建新的导弹拦截系统就不创建新的导弹拦截系统
    memset(f, 0x3f, sizeof f);
    
    for(int i=1; i<=cnt; ++i)
    {
        int x = a[i];
        int j = lower_bound(f, f+cnt, x)-f;
        f[j] = x;
    }
    res = 0;
    while(f[res]!=0x3f3f3f3f)   res++;
    cout<<res<<endl;
    
    return 0;
}

G - (数字三角形的变形) 计蒜客 - T2153

他山之玉

# include<iostream>
# include<algorithm>

using namespace std;

const int N = 30;

int n;
int dp[N<<1][N][N];     //总坐标是i,a的横坐标是j,b的横坐标是k
int g[N][N];

int main()
{
    cin>>n;
    int a, b, c;
    while(cin>>a>>b>>c, a||b||c)   g[a][b] = c;
    
    for(int i=2; i<=(n<<1); ++i)
    {
        for(int j=1; j<=n; ++j)
        {
            for(int k=1; k<=n; ++k)
            {
                if(j>=i || k>=i)   continue;
                if(j==k)
                {
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k]+g[j][i-j]);
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k-1]+g[j][i-j]);
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]+g[j][i-j]);
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1]+g[j][i-j]);
                }
                else
                {
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k]+g[j][i-j]+g[k][i-k]);
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k-1]+g[j][i-j]+g[k][i-k]);
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]+g[j][i-j]+g[k][i-k]);
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-1][k-1]+g[j][i-j]+g[k][i-k]);
                }
            }
        }
    }
    
    cout<<dp[n<<1][n][n]<<endl;
    
    return 0;
}

H - (用一下逆向思维,写法同上) 计蒜客 - T2091