JOJ水DP汇总

 花点时间整理下以前A掉的水题 , 巩固下基础 ,也希望能再细节处理上有所提高

可以用动态规划来解决的问题 要满足无后效性(DAG)、最优子结构

 

【无特殊说明都是JOJ的题】

/***********************************************************************************/

2526: medic采药问题, 经典01背包,不必装满

1维空间,用组合数学的生成函数思想更好理解一些

#include <cstdio>
#include <string.h>

const int maxn=102;
int w[maxn],v[maxn],ans[1005];
int main ()
{
    int t,m,i,j;
    while (scanf("%d%d",&t,&m)==2)
    {
        for (i=0 ; i<m ; ++i)
         scanf("%d%d",w+i,v+i);
        memset (ans,0,sizeof(ans));
        for (i=0 ; i<m ; i++)//0...N
         for (j=t ; j>=w[i] ; --j)//V...cost
         {
                ans[j]>?=ans[j-w[i]]+v[i];
         }
         //for (i=0 ; i<=t ; i++)
          printf("%d\n",ans[t]);
    }
    return 0;
}

/***********************************************************************************/

1424 1583 2201 1176 可看作质量为1的,完全背包计数问题,注意初始化,理解成生成函数更好些

#include<stdio.h> 
#include<memory.h> 
using namespace std;
const int maxn=7500;
int c[5]={1,5,10,25,50}; 
int v; 
int f[maxn]; 
 
int main() 
{ 
    memset(f,0,sizeof(f)); 
    f[0]=1; 
    for(int i=0;i<5;i++) 
    for(int j=c[i];j<=maxn;j++) 
    { 
        f[j]+=f[j-c[i]];//重要 
    } 
    while(scanf("%d",&v)==1) 
    { 
        printf("%d\n",f[v]); 
    } 
 
    return 0; 
} 

/***********************************************************************************/

 

POJ 2392 多重背包 模板

#include <cstdio>
#include <cstring>
#include <algorithm>
#define max(a,b) (a>b?a:b)

using namespace std;
const int maxn=500;
int dp[5][40050];

struct Item {
    int h,a,c;
}blo[maxn];

bool cmp(const Item & a , const Item & b)
{
    return a.a<b.a;
}

void O1Pack(int f[] , int c , int w , int V)
{
    for (int v=V ; v>=c ; --v)
        f[v]=max(f[v-c]+w , f[v]);
}

void ComPack(int f[] , int c , int w , int V)
{
    for (int v=c ; v<=V ; ++v)
        f[v]=max(f[v-c]+w , f[v]);
}

void MulPack(int f[] , int c , int w , int m , int V)
{
    if(c*m>=V)
    {
        ComPack(f , c , w , V);
        return ;
    }
    int k=1;
    while (k<m)
    {
        O1Pack(f , k*c , k*w , V);
        m-=k;
        k*=2;
    }
    O1Pack(f , c*m , w*m , V);
}

int main ()
{
    int c,w,m;
    while (~scanf("%d",&m))
    {
        memset (dp , 0 , sizeof(dp));
        for (int i=0 ; i<m ; ++i)
        {
            scanf("%d%d%d",&blo[i].h , &blo[i].a , &blo[i].c);
        }
        sort(blo , blo+m , cmp);
        for (int i=0 ; i<m ; ++i)
        {
            MulPack(dp[0] , blo[i].h , blo[i].h , blo[i].c , blo[i].a);
        }
        int ans=0;
        for (int i=0 ; i<=blo[m-1].a ; ++i)
        ans=max(dp[0][i],ans);
        printf("%d\n",ans);
    }
    return 0;
}

/***********************************************************************************/

HDU 1561泛化背包

/**O(n*V)泛化背包 算法来自论文【浅谈几类背包题】
4686406 2011-10-01 14:23:10 Accepted 1561 0MS 372K 1776 B G++ Geners **/
#include <cstdio>
#include <cstring>
#define max(a,b) (a>b?a:b)

const int maxn=205;
struct Edge {
    int v,next;
}edge[maxn];
int head[maxn],cnt;

void addedge(int u,int v)
{
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

int w[maxn],dp[maxn][maxn],node;
int n,m;

void dfs(int u , int c)
{
    for (int p=head[u] ; ~p ; p=edge[p].next)
    {
        int &v=edge[p].v;
        for (int j=0 ; j<c ; ++j)
            dp[v][j]=dp[u][j]+w[v];///取儿子的时候,父亲必须取,因此直接将父亲放入儿子,
        dfs(v , c-1);///更新儿子
        for (int j=1 ; j<=c ; ++j)///求2个有交集的泛化物品的并
            dp[u][j]=max(dp[u][j] , dp[v][j-1]);///更新儿子后,再赋值给父亲
    }///对于树的叶结点 我们可以把他处理成01Pack
}
int main ()
{
    while (scanf("%d%d",&n,&m),(n||m))
    {
        memset(head , -1 , sizeof(head));
        cnt=0;
        for (int i=1 ; i<=n ; ++i)
        {
            scanf("%d %d" , &node , w+i);
            addedge(node , i);
        }
        w[0]=0;
        memset(dp , 0 , sizeof(dp));
        dfs(0 , m);
        printf("%d\n",dp[0][m]);
    }
    return 0;
}


 

 

/***********************************************************************************/

 

 

2529 合唱 最长单调序列变形

用OPT1存上升的,OPT2存下降的,则第I个人的最大为OPT1[I]+OPT[I] -1  时间n^2 空间n

因为每个数都包含自己本身,所以初始化全为1。

/***********************************************************************************/

2511 经典数塔问题,从下往上搜

opt[i][j]=num[i][j];

for i m..0

for j i..0

opt[i][j]+=opt[i+1][j]>?opt[i+1][j+1];

 

/***********************************************************************************/

1826 三角剖分  有公式的 可以推

f[2]=1;f[3]=1; f[i]=4*f[i-1]-6*f[i-1]/(i-1);

/***********************************************************************************/

1968 最长公共子序列 时间 n^2 空间n^2

阶段不是很明显,opt[i][j]表示从起点开始 str1长度为i,str2长度为j的最长公共子序列

#include <stdio.h>
#include <memory.h>
#include <string.h>
const int maxn=200;
char str1[maxn],str2[maxn];
int opt[maxn][maxn];
int same(int i,int j)
{
    if(str1[i]==str2[j])
     return 1;
    return 0;
}
int main ()
{
    int i,j;
    while (scanf("%s%s",str1,str2)!=EOF)
    {
        memset (opt,0,sizeof(opt));
        opt[1][1]=same(0,0);
        int len1=strlen(str1);
        int len2=strlen(str2);
        int ans=0;
        for (i=1 ; i<=len1 ; i++)
         for (j=1 ; j<=len2 ; j++)
         {
             opt[i][j]=opt[i][j-1];
             if(opt[i][j]<opt[i-1][j])opt[i][j]=opt[i-1][j];
              if(opt[i][j]<(opt[i-1][j-1]+same(i-1,j-1)))
               opt[i][j]=(opt[i-1][j-1]+same(i-1,j-1));
         }
        printf("%d\n",opt[len1][len2]);
    }
    return 0;
}


 

/***********************************************************************************/

 1995 最大子段和 时间n

用sum寄存 前缀和,当sum<0时,清零,同时要记录扫描过程sum的最大值 以及数值的最大值,因为有可能有全是负数的情况,但这时sum的最大值默认为0

2058 一样的 多了些条件  记录区间什么的

/***********************************************************************************/

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值