NYOJ 动态规划

题1:NYOJ 10(滑雪),前面的博文-动态规划中有。

题2:NYOJ 15(括号匹配),放在区间动态规划中。

题3:NYOJ 16(矩阵嵌套),大概意思给你n个矩阵,这些矩阵相互嵌套,问能够嵌套的最大个数,直接按长度从大到小排序,长度相等则按宽度从大到小排序,由于能够嵌套满足:长1<长2,宽1<宽2,排序后就成了最长下降子序列了。状态转移方程:DP[i]=max(DP[i],DP[j+1])(0<j<i,M[i].b<M[j].b&&M[i].a!=M[j].a)。

 
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX=1010;
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
int n,DP[MAX];
struct Matrix{
    int a,b;
    bool operator<(const Matrix& m) const
    {   if(a!=m.a) return a>m.a;
        if(a!=m.a&&b>m.b) return b>m.b;
    } 
}M[MAX];
int main()
{   int Case,l,w;
    scanf("%d",&Case);
    while(Case--)
    {   scanf("%d",&n);
        for(int i=0;i<n;i++)
        {   scanf("%d%d",&l,&w);
            M[i].a=max(l,w);
            M[i].b=min(l,w);
        }
        sort(M,M+n);
        fill(DP,DP+MAX,1);
        for(int i=0;i<n;i++)
            for(int j=0;j<i;j++)
                if(M[i].b<M[j].b&&M[i].a!=M[j].a) DP[i]=max(DP[i],DP[j]+1);//要加上M[i].a!=M[j].a这个条件
        printf("%d\n",*max_element(DP,DP+n));        
    }
    return 0;
}
        

题4:NYOJ 17(单调递增子序列)。动态转移方程:DP[i]=max(DP[i],DP[j]+1);(0<j<i,s[i]>s[j])。

#include<iostream>
#include<cstring>
#include<cstdio> 
#include<algorithm>
using namespace std;
const int MAX=10010;
#define max(a,b) a>b?a:b
char s[MAX];
int DP[MAX];
int main()
{   int Case;
    scanf("%d",&Case);
    getchar(); 
    while(Case--)
    {   gets(s);
        fill(DP,DP+MAX,1);
        for(int i=0;i<strlen(s);i++)
            for(int j=0;j<i;j++)
                if(s[i]>s[j]) DP[i]=max(DP[i],DP[j]+1);
        printf("%d\n",*max_element(DP,DP+MAX));        
    }
    return 0;
} 

题5:NYOJ 18(数字塔),前面的文章动态规划中有。

题6:NYOJ 36(最长公共子序列),设DP[i][j]为字符串a的前i个字符与字符串b的前j个字符的最长公共子序列。

①、当i,j位置的字符相同,即a[i-1]=b[j-1],那么有DP[i][j]=DP[i-1][j-1]+1

②、若是a[i-1]!=b[j-1],那么有:DP[i][j]=max(DP[i][j-1],DP[i-1][j])

#include<iostream>
#include<cstring>
#include<string>
using namespace std;
const int MAX=1010;
#define max(a,b) (a)>(b)?(a):(b)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
string a,b;
int DP[MAX][MAX];
int main()
{   int Case;
    cin>>Case;
    cin.get();
    while(Case--)
    {   cin>>a>>b;
        CLR(DP,0);
        int n=a.length(),m=b.length();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {   if(a[i-1]==b[j-1]) DP[i][j]=DP[i-1][j-1]+1;
                else DP[i][j]=max(DP[i][j-1],DP[i-1][j]);
            }
        cout<<DP[n][m]<<endl;    
    }
    return 0;
}

题7:NYOJ 37(回文字符串),就是一个字符串,从左到右读和从右到左读是完全一样。那么可以将字符串反转过来(可用string中的reverse函数),然后寻找两个字符串的最长公共子序列。那结果就是字符串长度-最长公共子序列的长度,就是要添加的字符串长度,具体实现和上题一样。

题8:NYOJ 44(最大子串和),用max保存最大子串和,比较简单具体自己看。

#include<iostream>
#include<cstdio>
using namespace std;
const int MAX=1000010; 
int n,a[MAX];
int main()
{   int Case;
    scanf("%d",&Case);
    while(Case--)
    {   scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        int max=a[0],sum=0;
        for(int i=0;i<n;i++)
        {   sum+=a[i]; //统计某一区间的子串和
            if(max<sum) max=sum;
            if(sum<0) sum=0; 
        }    
        printf("%d\n",max);
    } 
    return 0; 
} 

查看了排名第一的谢武强的代码,他的代码在输入上利用了自己编写的一个函数来实现数据量比较大的输入,我只写下输入的函数。

int Read()
{   char ch;
	int num=0,flag=0;
	ch=getchar();
	if(ch=='-')//判断正负数
	{   flag=1;
		ch=getchar();
	}
	while(ch!=10&&ch!=' ')
	{   num=num*10+ch-'0' ;
		ch=getchar();
	}
	return flag?-num:num;
}

题9:NYOJ 49(开心的小明),01背包,设DP[j]为当价值为j的时候价值与重要度的乘积总和的最大值,那么DP[j]=max(DP[j],DP[j-v]+v*Ip(j>=v)(v为价值,Ip为对应的重要度),代码略。

NYOJ 325(zb的生日)数据量比较小,可以直接搜索,搜索代码见DFS(3),也可以用01背包做,假设两堆西瓜重量分别为a和b,那么两堆之差为:|a-b|=|a-(sum-a)|=|2a-sum|,用DP[j]表示当容量为j时最大的装载量,那么DP[sum/2]就表示其中一堆西瓜的数量,因为两堆西瓜肯定有一堆>=sum/2。那么我就尽量在容量为sum/2中尽量的放入西瓜,使得容量为sum/2的时候西瓜的重量最大,那结果就为:sum-2*DP[sum/2],动态转移方程为:DP[j]=max(DP[j],DP[j-a[i]]+a[i])(j>=a[i]);---其中a[i]为第i个西瓜的重量,sum为西瓜的总重量。下面为NYOJ 325的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAX=100010;
#define max(a,b) (a)>(b)?(a):(b)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int n,sum,a[MAX],DP[MAX];
int main()
{    while(scanf("%d",&n)!=EOF)
    {   sum=0;
        for(int i=0;i<n;i++)
        {   scanf("%d",&a[i]);
            sum+=a[i];        
        }
        CLR(DP,0);
        for(int i=0;i<n;i++)
            for(int j=sum/2;j>=0;j--)
                if(j>=a[i]) DP[j]=max(DP[j],DP[j-a[i]]+a[i]); 
        printf("%d\n",sum-2*DP[sum/2]);        
    }
    return 0;
}        

NYOJ 456(我的邮票分你一半),这个题目数据量稍大,但是用搜索会超时,只能用01背包。代码和上面的差不多改下就行了。

题10:NYOJ 61(传纸条)。代码见多进程DP。
题11:NYOJ 76(超级台阶) ,设Fib[i]为台阶为i是的走法,那么可以从i-1的台阶走一步到达i,也可以从i-2位置的台阶一次走二步,所以有:Fib[i]=Fib[i-1]+Fib[i-2]。代码略。

题12:NYOJ 79(拦截导弹),最长下降子序列,代码略。
题13:NYOJ 81(炮兵阵地) ,完全不会动手,过~传说中为状态压缩DP。

题14:NYOJ 104(最大子矩阵和),利用最大子串和(NYOJ 44),可做模板:

#include<iostream>  
#include<cstring>   
#include<cstdio>   
using namespace std;  
const int MAX=110;  
#define CLR(arr,val) memset(arr,val,sizeof(arr))    
int num,n,m,map[MAX][MAX];  
int Maxsum(int a[])  
{   int sum=0,max=a[0];  
    for(int i=0;i<m;i++)  
    {   if(sum>0) sum+=a[i];  
        else sum=a[i];  
        if(sum>max) max=sum;  
    }  
    return max;  
}  
int Matrix()   
{   int max=map[0][0],temp[MAX];  
    for(int i=0;i<n;i++)  
    {   CLR(temp,0);  
        for(int j=i;j<n;j++)  
        {   for(int k=0;k<m;k++)  
                temp[k]+=map[j][k];       
            int sum=Maxsum(temp);  
            if(sum>max) max=sum;  
        }   
    }          
    return max;  
}  
int main()  
{   scanf("%d",&num);  
    while(num--)  
    {   scanf("%d%d",&n,&m);  
        for(int i=0;i<n;i++)  
            for(int j=0;j<m;j++)  
                scanf("%d",&map[i][j]);  
        cout<<Matrix()<<endl;  
    }  
    return 0;  
} 

题15:NYOJ 110(剑客决斗),

题16:NYOJ 119(士兵杀敌),RMQ算法,前面的博文中有代码。

题17:NYOJ 171(聪明的KK),弄懂了NYOJ 61(传纸条),这个就是一盘菜,代码看61的解题报告后,自己写吧,代码略。 


 

你可能感兴趣的:(NYOJ 动态规划)