文章目录
题1:最大连续子序列和
http://codeup.cn/status.php?user_id=20170613218&cid=100000626&fixed=
#include<bits/stdc++.h>
using namespace std;
const int maxv=10010;
int n,str[maxv],temp,ans,ansr,ansl;
int main() {
while(scanf("%d",&n)!=EOF&&n) {
// bool flag=false;
temp=ans=0;
ansr=n-1;ansl=0;
int l=0;
for(int r=0; r<n; r++) {
scanf("%d",&str[r]);
// if(str[i]>0)flag=true;
temp+=str[r];
if(temp<0){
l=r+1;
temp=0;
}
else if(temp>ans){
ansl=l;
ansr=r;
ans=temp;
}
}
printf("%d %d %d\n",ans,str[ansl],str[ansr]);
}
return 0;
}
题2:最长不下降子序列(LIS)
http://codeup.cn/problem.php?cid=100000627&pid=0
注意
用lower_bound 把最大子序列插入dp相应的位置,时间复杂度(n*log^n)
不能用替换和增长,因为解决不了除最末元素的替换;
问题:只能替换最末元素无法替换之前的元素,会出错,例:
1 2 3 789 8697 4 5 6 7 8就只能判断为最大长度5
#include<bits/stdc++.h>
using namespace std;
const int maxv=10010;
const int INF=1e6;
int n,dp[maxv],a[maxv];
int main() {
while(scanf("%d",&n)!=EOF&&n) {
fill(dp,dp+n,INF);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
*lower_bound(dp,dp+n,a[i])=a[i];
printf("%d\n",(lower_bound(dp,dp+n,INF)-&dp[0]));
}
return 0;
}
题3:最长公共子序列(LCS)
http://codeup.cn/status.php?user_id=20170613218&cid=100000628&fixed=
#include<bits/stdc++.h>
using namespace std;
const int maxv=110;
const int INF=1e6;
int dp[maxv][maxv],n,m;
char A[maxv],B[maxv];
int main() {
while(scanf("%s %s",A+1,B+1)!=EOF) {
n=strlen(A+1);
m=strlen(B+1);
fill(dp[0],dp[0]+maxv*maxv,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(A[i]==B[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
printf("%d\n",dp[n][m]);
}
return 0;
}
题4:最长回文子串
http://codeup.cn/problem.php?cid=100000629&pid=0
注意
①首先解决“判断时忽略标点,输出进却要按原样”的问题? 可以用一个简单的方法:预处理。构造一个新字符串,不包含原来的标点符号,而且所有字符变成大写(顺便解决了大小写的问题)。 用到的函数:
(1)isalpha( c )用来检查c是否为字母,如果是字母,则返回1;否则返回0。
(2)isdigit( c )用来检查c是否为数字(0~9),如果是数字,则返回1;否则返回0。
(3)toupper( c )用来将c字符转换为大写字母,返回c对应的大写字母。
(4)tolower( c )用来将c字符转换为小写字母,返回c对应的小写字母。
②最后的问题:原样输出。
由于在求max值时,不知道s[i]和s[j]在原串buf中的位置。因此,必须增加一个数组p,用p[i]保存s[i]在buf中的位置。在预处理得到,然后在更新max的同时把p[i]和p[j]保存到x和y,最后输出buf[x]到buf[y]中的所有字符。
③应该输出最长的回文串,如果有多个,输出起始位置最靠左的。
解决这一点的办法就是枚举其右端点,从最右向左枚举
#include<bits/stdc++.h>
using namespace std;
const int maxv=5010;
int n,m,pre[maxv];//n是原字符串长,m是忽略空格标点后的字符串长
char str[maxv],Copy[maxv];
//Copy是统一大小写,忽略标点空格的str,pre是记录每位Copy在str中的下标位置
bool dp[maxv][maxv];//左端点->右端点
int main() {
while(gets(str)!=NULL) {
int ans=1,start=0;
n=strlen(str);
m=0;
for(int i=0; i<n; i++) {
if(!isalpha(str[i])&&!isdigit(str[i]))//忽略标点和空格
continue;
Copy[m]=tolower(str[i]);
pre[m++]=i;
}
fill(dp[0],dp[0]+m*m,false);
//边界:自身为长度为1的回文串,相邻为长度为2的回文串
for(int i=m-1; i>=0; i--) {
dp[i][i]=true;
if(Copy[i]==Copy[i-1]&&i>0) {
dp[i-1][i]=true;
start=i-1;
ans=2;
}
}
//DP状态转移方程:
for(int L=3; L<m; L++) { //L为回文串长度
for(int j=m-1; j+1-L>=0; j--) { //j+1-L为左端点,j为右端点,在0~n-1范围内(j-i=L-1)
int i=j+1-L;
if(Copy[i]==Copy[j]&&dp[i+1][j-1]) {
start=i;
ans=L;
dp[i][j]=true;
}
}
}
if(m!=0){//若有字母才有子串
for(int i=pre[start]; i<=pre[start+ans-1]; i++)
printf("%c",str[i]);
printf("\n");
}
}
return 0;
}
题5:DAG最长路
问题 A: 矩形嵌套
http://codeup.cn/problem.php?cid=100000630&pid=0
#include<bits/stdc++.h>
using namespace std;
const int maxv=1010;
int N,n;
int dp[maxv],maxn;
vector<int>mp[maxv];//mp图
struct REC{
int x,y,c;
}rec[maxv];//rec矩形边宽
bool cmp(REC a,REC b)
{
if(a.c!=b.c)
return a.c<b.c;
if(a.y!=b.y)
return a.y<b.y;
return a.x<b.x;
}
void fix(int i,int j)
{
if(rec[i].x<rec[j].x&&rec[i].y<rec[j].y){
mp[i].push_back(j);
}
}
int DP(int u)
{
if(dp[u]>0)return dp[u];
for(int i=0;i<mp[u].size();i++){
int v=mp[u][i];
dp[u]=max(dp[u],DP(v)+1);
}
if(dp[u]>maxn)
maxn=dp[u];
return dp[u];
}
int main() {
int x,y;
scanf("%d",&N);
while(N--) {
scanf("%d",&n);
for(int i=0;i<n;i++){
mp[i].clear();
scanf("%d %d",&x,&y);
if(x>y)swap(x,y);
rec[i].x=x;
rec[i].y=y;
rec[i].c=x*y;
}
sort(rec,rec+n,cmp);
for(int i=0;i<n-1;i++){//小
for(int j=i+1;j<n;j++){//大
fix(i,j);
}
}
fill(dp,dp+n,0);
maxn=0;
for(int i=0;i<n;i++){
DP(i);
}
printf("%d\n",maxn+1);
}
return 0;
}
题6:背包问题
01背包问题
问题 A: 装箱问题
http://codeup.cn/problem.php?cid=100000631&pid=0
//01背包
#include<bits/stdc++.h>
using namespace std;
const int maxv=20010;
int V,n,w[maxv];
int dp[maxv];
int main(){
while(~scanf("%d %d",&V,&n)){
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
fill(dp,dp+V+1,0);//边界
for(int i=1;i<=n;i++){
int v;
for(v=V;v>=w[i];v--)
dp[v]=max(dp[v],dp[v-w[i]]+w[i]);//状态转移方程
}
printf("%d\n",V-dp[V]);
}
return 0;
}
问题 B: 采药
http://codeup.cn/problem.php?cid=100000631&pid=1
//01背包
#include<bits/stdc++.h>
using namespace std;
const int maxv=1010;
int V,n,w[maxv],c[maxv];
int dp[maxv];
int main(){
while(~scanf("%d %d",&V,&n)){
for(int i=1;i<=n;i++)
scanf("%d %d",&w[i],&c[i]);
fill(dp,dp+V+1,0);//边界
for(int i=1;i<=n;i++){
int v;
for(v=V;v>=w[i];v--)
dp[v]=max(dp[v],dp[v-w[i]]+c[i]);//状态转移方程
}
printf("%d\n",dp[V]);
}
return 0;
}
完全背包问题
***问题 C: 货币系统
http://codeup.cn/problem.php?cid=100000631&pid=2
母牛们不但创建了他们自己的政府而且选择了建立了自己的货币系统。
[In their own rebellious way],,他们对货币的数值感到好奇。
传统地,一个货币系统是由1,5,10,20 或 25,50, 和 100的单位面值组成的。
母牛想知道有多少种不同的方法来用货币系统中的货币来构造一个确定的数值。
举例来说, 使用一个货币系统 {1,2,5,10,…}产生 18单位面值的一些可能的方法是:18x1, 9x2, 8x2+2x1, 3x5+2+1,等等其它。
写一个程序来计算有多少种方法用给定的货币系统来构造一定数量的面值。
保证总数将会适合long long (C/C++) 和 Int64 (Free Pascal)。
省题
设dp[i][v]为前i种货币可能构造出v价值的方法量
两种策略:取或不取
(1)不取,那么方法量同 前i-1种货币可能构造出v价值的方法量相同
(2)取,那么就是有w[i]的货币是由第i种货币实现的,剩下的就是 前i-1种货币可能构造出v-w[i]价值的方法量
所以,两种策略相加就是前i种货币可能构造出v价值的方法量
动态转移方程:
dp[i][v]=dp[i-1][v]+dp[i-1][v-w[i]]
(i∈1 ~ n,v∈w[i]~V)
由于每次求dp[i][v]只需要i-1中的v和其左边的v-w[i] 所以可以通过滚动数组的方法降维,v需要正向枚举
即:
dp[v]=dp[v]+dp[v-w[i]]
(v∈w[i]~V)
边界:
dp[w[i]]=1;//i∈1 ~ n,取得货币自己本身大小,至少有取一个自己这一种,其他的初始化为0
(i∈1 ~ n)
注意
(1)范围上: (1<= V<=25) (1<= N<=10,000)
所以存储货币系统的w[i]不需要很大,而存储可获得v价值的方法量dp[v]需要存储比较大,分设maxn=30,maxv=1e5+10
(2)保证总数将会适合long long (C/C++) 和 Int64 (Free Pascal)。
这提示了你dp数组需要设置long long型来存储
AC代码
//完全背包
#include<bits/stdc++.h>
using namespace std;
const int maxv=1e5+10;
const int maxn=30;
int V,n,w[maxn];
long long dp[maxv];
int main(){
while(scanf("%d %d",&n,&V)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
sort(w+1,w+n+1);
fill(dp,dp+V+1,0);//边界
//状态转移方程
for(int i=1;i<=n;i++){
dp[w[i]]+=1;
for(int v=w[i];v<=V;v++)
dp[v]+=dp[v-w[i]];//在原来的基础方法上再加上出现有新的方法是数量
//dp[8],w[i]=5时,dp[8]+=dp[3],用一个5+达到3所用的方法量
//dp[11],w[i]=5时,dp[11]+=dp[6],dp[6]是已经累加过有一个5的的方法
}
printf("%lld\n",dp[V]);
}
return 0;
}