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 肯定是有路的
#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;} 一些比较特殊的动态规划这道题一直WRONG a不太理解
直线的交点数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; }