二探动态规划!

1.23 搞清了为啥0 1背包可以空间优化以及内层循环为啥是逆序的

上博客http://blog.csdn.net/xiajiawei0206/article/details/19933781讲的巨清楚hh

最长上升子序列  http://blog.csdn.net/yopilipala/article/details/60468359

#include<iostream>
#include<algorithm>
#include<cstring>
const int  INF=0x3f3f3f3f;
/*函数lower_bound()在first和last中的前闭后开区间进行二分查找,
返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置
注lower_bound返回的是一个地址*/
/*一个循环加上一个二分查找决定他的  时间复杂度O(nlgn)*/
using namespace std;
int main()
{
	int n,num[100100],lis[100100],len;
	while(cin>>n&&n)
	{
		len=0;
		memset(lis,0,sizeof(lis));
		for(int i=0;i<n;i++)
		{
			cin>>num[i];
		}
		lis[0]=-INF;
		for(int i=1;i<n;i++)
		{
			if(num[i]>lis[i])
			   lis[++len]=num[i];//如果num比lis[len]选择的终点大,则可以放入lis,即新的终点。
			else
			   {
			   	int t=lower_bound(lis,lis+len,num[i])-lis;
			   	    lis[t]=num[i];//!!!!
			   	  
			   }
		}
	}
}


看了动态规划一的题目还差几道题没做出来 其中做出来的也有很多错误代码已经标记

LCS求公共子序列 用滚动数组压缩空间

#include<iostream>
#include<cstring>
#include<algorithm>
#define MAX(a,b) ((a)>(b)?(a):(b)) //记得要用括号括起来 
using namespace std;
#define N 1005
string s1; // s1.size()  这个函数要学会返回字符数量 
string s2;
int a[N],b[N];
int main()
{
	while(cin>>s1>>s2)
	{
	  int 	len1=s1.size();
	  int 	len2=s2.size();
	  memset(a,0,sizeof(a));
	  memset(b,0,sizeof(b));
       for(int i=1;i<=len1;i++)
       {
       	for(int j=1;j<=len2;j++)
            if(s1[i-1]==s2[j-1])
               b[j]=a[j-1]+1;
            else
               {
               b[j]=MAX(a[j],b[j-1]);
			   }
		for(int j=1;j<=len2;j++)
		     a[j]=b[j];
	   }
          
			   
	    cout<<a[len2]<<endl ; 
	}
	return 0;
 } 
LCS 输出其中的一个顺序  在动态规划里输出

/*输出一个最长公共子序列 在 动态规划中实现  这是一种正推的方法
    当str1[i]=str2[j] 输出str1[i] 
   用来标记已经取出的 j,
		对于相同的i,如果出现了 两个b[j]与a[i]相同则取第一个 
		对于不同的i,已经取过的j 就不要再取了
*/
#include<iostream>//正确代码 
#include<cstring>
#include<algorithm>
int c[105][105]={0};//用来储存最长子序列 个数 
using namespace std;
int main()
{
	string str1;
	string str2;
	while(cin>>str1>>str2)
	{
		int t=0;//后面输出的j要比t要大 
		int k[105];/*/用来标记已经取出的 j,
		对于相同的i,如果出现了 两个b[j]与a[i]相同则取第一个 
		对于不同的i,已经取过的j 就不要再取了*/
		int n=str1.size();//行数
	int m=str2.size();//列数 
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++)
	      if(str1[i]==str2[j])
	      {
	      	c[i][j]=c[i-1][j-1]+1;
	      	if(j>t)
	      	{
	      		cout<<str2[j-1];
				  t=j;	
			  }
	      	
		  }
	         
	      else
	          c[i][j]=max(c[i-1][j],c[i][j-1]);

	cout<<c[n][m]<<endl; 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
	    {
	    	cout<<c[i][j]<<" ";
		}
		cout<<endl;
	}
    }

	
	
   
	return 0;
} 
法二:自己按照PPt上写的
/*
如果要输出一个最长公共子序列,又该如何处理?
方法:x从1到k,扫描f(i, j)即可。
输出第一个c[i][j]变化的a[i]; 
*/
#include<iostream>//正确代码 
#include<cstring>
#include<algorithm>
int c[105][105]={0};//用来储存最长子序列 个数 
using namespace std;
int main()
{
	string str1;
	string str2;
	while(cin>>str1>>str2)
	{
		int n=str1.size();//行数
	int m=str2.size();//列数 
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++)
	      if(str1[i]==str2[j])
	         c[i][j]=c[i-1][j-1]+1;
	      else
	          c[i][j]=max(c[i-1][j],c[i][j-1]);

	cout<<c[n][m]<<endl; 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
	    {
	    	cout<<c[i][j]<<" ";
		}
		cout<<endl;
	}
	    
	int i,j=1;
	int t=1;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		if(t==c[i][j])
		 {
		 	cout<<str1[i-1];
		 	t++;
		 	break;
		 }
		 if(t==c[n][m]+1)
		   break;
	}
	}
	
	
   
	return 0;
} 
LCS的一些同样思想的动态规划  动态规划二1001到1004

1003

#include<iostream>
/*
学习对表格的处理<map>头文件
	int t=mapp[str1[i-1]];
		   	int w=mapp[str2[j-1]];
		   	dp[i][j]=MAX(dp[i-1][j-1]+v[t][w],MAX(dp[i-1][j]+v[t][4],dp[i][j-1]+v[4][w]));*/
#include<map>
#define MAX(a,b) ((a>b)?(a):(b))
using namespace std;
int v[5][5]=
{{5,-1,-2,-1,-3},
  {-1,5,-3,-2,-4},
  {-2,-3,5,-2,-2},
  {-1,-2,-2,5,-1},
  {-3,-4,-2,-1,0},              
};
map<char,int> mapp;//map 点对关系 字符跟数值对应起来  字符可以做下标 通过映射关系 编程效率可以更快 
int main(){
	ios::sync_with_stdio(false);
	mapp['A']=0;
	mapp['C']=1;
	mapp['G']=2;
	mapp['T']=3;
	mapp['_']=4;
	int t;
	cin>>t;
	int len1;//str1的长度
	int len2; 
	string str1;
	string str2;
	while(t--)
	{
		cin>>len1;
		cin>>str1;
		cin>>len2;
		cin>>str2;
		const int n=str1.size()+1;
		const int m=str2.size()+1;
		int dp[n][m]={0};
		for(int i=1;i<n;i++)
		  dp[i][0]=dp[i-1][0]+v[mapp[str1[i-1]]][4];
		for(int j=1;j<m;j++)
		  dp[0][j]=dp[0][j-1]+v[4][mapp[str2[j-1]]];
		for(int i=1;i<n;i++)
		   for(int j=1;j<m;j++)
		   {
		   	int t=mapp[str1[i-1]];
		   	int w=mapp[str2[j-1]];
		   	dp[i][j]=MAX(dp[i-1][j-1]+v[t][w],MAX(dp[i-1][j]+v[t][4],dp[i][j-1]+v[4][w]));
		   }
 		cout<<dp[n-1][m-1]<<endl;	 		
	} 
	return 0;
}


动态规划:二维数组

例题数塔问题

/* 初始化的问题  最左边 一条线 还有最右边的一条线把以上各个数字相加*/
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
//m[ i ][ j ] = max { m[ i -1][ j-1 ] + m[ i-1][ j ] } + a[ i ][ j ] 顺推 
using namespace std;
//d[i][j]表示第i层的第j个点往下走可以获得的最大和,由于只能往左下或者右下走, 
//所以d[i][j]=max(d[i+1][j+1],d[i+1][j]),这个东西叫做状态转移方程,是动态规划的核心所在。逆推 
//这样相当于从下往上贪心,是不会出问题的。这也就包含了一个“全局最优解一定包含局部最优解”的思想。
int  a[105][105]={0};
int b[105][105]={0};//路径的总和 
int max(int a,int b)//返回a b 中的最大值 
{
	return a>b?a:b;
 }
 int cmp(int a,int b)
 {
 	return a>b;
 }
int main()
{

    int t;//测试的个数 
	int n;
	cin>>t;
	while(t--)
	{
	    cin>>n;
		int i,j;
		for( i=1;i<=n;i++)
		  for(j=1;j<=i;j++)
		    cin>>a[i][j];
		    b[1][1]=a[1][1];
		for( i=1;i<=n;i++)
		{
			 b[i][1]=b[i-1][1]+a[i][1];
			 b[i][i]=b[i-1][i]+a[i][i];
		}

		for(i=1;i<=n;i++)
		  for(j=1;j<=i;j++)
		     b[i][j]=a[i][j]+max(b[i-1][j-1],b[i-1][j]);//这个是逆序 可以采用覆盖a 原来的值来代表总和 
		int t=b[n][1];	//b[i][j]=a[i][j]+max(b[i+1][j],b[i+1][j+1]); 此为错误代码 
		for(i=1;i<=n;i++)
		    if(b[n][i]>t)
		       t=b[n][i];
		    cout<<t<<endl;
	}

	 
	return 0;
}
数塔的变形  免费馅饼
#include<iostream>
using namespace std;
#include<string.h>
#include<cstring>
#include<stdio.h>
int value[100004][11];
long long  dp[100004][11];
int num,T,X,i,j,maxT=0;
void dps(int x,int t);
int main()
{
    while(scanf("%d",&num)&&num)
    {
        memset(value,0,sizeof(value));
        memset(dp,0,sizeof(dp));
        maxT=0;
        for(i=0;i<num;i++)
        {
           scanf("%d%d",&X,&T);
           maxT=max(maxT,T);
           value[T][X]++;
        }

        dp[1][4]=value[1][4],dp[1][5]=value[1][5],dp[1][6]=value[1][6];
        for( i=2;i<=maxT;i++)
        {
            for(j=0;j<=10;j++)
            {
               dp[i][j]=dp[i-1][j];
               if(j>0)
                dp[i][j]=max(dp[i][j],dp[i-1][j-1]);
               if(j<10)
                dp[i][j]=max(dp[i][j],dp[i-1][j+1]);
               dp[i][j]+=value[i][j];
            }
        }

     long long maxx=0;
     for(i=0;i<=10;i++)
        maxx=max(dp[maxT][i],maxx);
    cout<<maxx<<endl;
    }
    return 0;
}


变形题二 http://blog.csdn.net/ACM_DavidCN/article/details/5976588

#include<iostream>//
a[j][i]&&(f[j]+inter[i]>f[i]) 这个如果成立的话 f[j]肯定不为0 说明从 点1到点j 肯定是有路的 
这道题一直WRONG a不太理解
#include<cstring>using namespace std;int f[102]={0};//到达 第i个城市之后的最大兴趣值 如果第i个城市无法从第一个城市到达则为0 初始的时候都为0 int a[102][102];//用来表示是否可达 int inter[102];//各个城市的兴趣值 int pre[105];//用来记录每一次发生状态转移 时 到第i个点的前一个点void out (int i) { if (i==1) { printf("1"); return ; } out(pre[i]); printf("->%d",i); } int main(){ int t=1;int cases;//测试的个数 cin>>cases;int n;//城市数int m;//可到达的城市的线路数 while(cases--){ memset(pre,0,sizeof(pre)) ;memset(a,0,sizeof(a));memset(f,0,sizeof(f));memset(inter,0,sizeof(inter));cin>>n;for(int i=1;i<=n;i++)//读取每个城市的兴趣值 一开始自己写成了从i=0开始 cin>>inter[i];cin>>m;for(int i=0;i<m;i++) { int q,w; cin>>q>>w; a[q][w]=1;//能够到达的定义为1 否则为0 } f[1]=0;for(int i=2;i<=n+1;i++){for(int j=1;j<i;j++){if(a[j][i]&&(f[j]+inter[i]>f[i])) { f[i]=f[j]+inter[i]; pre[i]=j; } } } int _max=f[1];for(int i=1;i<=m;i++) if(f[i]>_max) _max=f[i];printf("CASE %d#\n",t++); printf("points : %d\n",f[n+1]); printf("circuit : "); out(pre[n+1]); printf("->%d\n",1); } return 0;}


一些比较特殊的动态规划

 直线的交点数http://blog.csdn.net/qaz135135135/article/details/52163461

  m条直线的交点方案数 分类

  =m-r)条平行线与r条直线交叉的交点数

    + r条直线本身的交点方案

  =m-r)*r+r条之间本身的交点方案数(0<=r<m

*(m-r)条平行线与r条直线交叉的交点数
    + r条直线本身的交点方案
  =(m-r)*r+r条之间本身的交点方案数(0<=r<m)
*/
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	int f[21][192];
    int i,j,n;
    memset(f,0,sizeof(f));
    for(i=0;i<=20;i++)
      f[i][0]=1;
    for(n=2;n<21;n++)//从两条直线开始求 
       for(i=n-1;i>=1;i--) //取出n-i条 
         for(j=0;j<191;j++)
          if(f[n-i][j]==1)
             f[n][j+i*(n-i)]=1;
    while(cin>>n&&n)
	{
		cout<<"0";
		for(j=1;j<=n*(n-1)/2;j++)
		  if(f[n][j]==1)
		     cout<<" "<<j;//哇 这里输出的是 j 不是f[n][j] 
		cout<<endl; 
	 } 
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值