emmm,这个系列似乎没那么水了><
DP系列,虽然个人觉得第一题没那么典型,不过确实也用到了自底向上的递推。
hdoj 2062
思路:递推求解每个分组中数的个数
注意:1、long long 和 %lld(Runtime Error(ACCESS_VIOLATION) or WA...)
2、字典序
3、m要减去空集的情况
//hdoj 2062
#include <stdio.h>
//in lexicography order按字典序
int main(){
int n;
long long m;
long long cnt[21];
int val[21];
cnt[1]=1;
for(int i=2;i<21;++i){//递推!!!
cnt[i]=(i-1)*cnt[i-1]+1;//n=i时,每个分组中的数的个数
}
while(scanf("%d %lld",&n,&m)!=EOF){//%lld!!
for(int i=0;i<21;++i){
val[i]=i;//字典中的元素
}
while(n&&m){
int a=(m-1)/cnt[n]+1;//第a组 或
//int a=m/cnt[n]+(m%cnt[n]==0?0:1);
printf("%d",val[a]);
//后面不能再出现这个元素,字典中这个元素后面的元素前移
for(int i=a;i<n;++i){/////下一轮只要用到n-1个数字
val[i]=val[i+1];
}
m-=(a-1)*cnt[n]+1;//+1代表空的情况!
//if(m==0) printf("\n");
//else printf(" ");
putchar(m==0?'\n':' ');///
--n;
}
}
return 0;
}
hdoj 1087
DP 空间换时间
扫描二维码关注公众号,回复:
2106040 查看本文章
//hdoj 1087 求序列中最大的递增子序列累计和
#include <vector>
#include<algorithm>////max,max_element
#include<stdio.h>
//#include <math.h>
//#include <cmath>
using namespace std;
int main(){
int n;//31MS 1888K
while(scanf("%d",&n),n){
vector<int> v(n);
for(int i=0;i<n;++i){
scanf("%d",&v[i]);
}
if(n==1){
printf("%d",v[0]);
continue;
}
vector<long long> maxv(n,0);
vector<long long> sum(n,0);
vector<int> tail(n,0);
sum[0]+=v[0];
maxv[0]=max(sum[0],maxv[0]);
tail[0]=v[0];
for(int i=1;i<n;++i){
for(int j=i-1;j>=0;--j){//遍历递减序列(46MS 1916K)//long long n;vector<long long> v(n);
if(v[i]-v[j]>0&&(v[j]>tail[i])){
tail[i]=v[j];
sum[i]=v[i]+maxv[j];
maxv[i]=max(maxv[i],sum[i]);
}
}
if(maxv[i]==0) maxv[i]=v[i];
}
printf("%lld\n",*max_element(maxv.begin(),maxv.end()));//*max_element!!!
}
return 0;
}
hdoj 1203
DP 01背包+乘积最小
用一维数组改善空间,循环时仍用两重循环,注意j要逆序!(参考这篇博客)
//hdoj 1203 01背包+乘积最小
#include <vector>
#include<algorithm>
#include<iostream>
#include <iomanip>///设置格式
#include <stdio.h>//格式输出更方便
using namespace std;
int main(){
int n,m;
//cout<<setiosflags(ios::fixed)<<setprecision(1);//保留一位小数
//while(cin>>n>>m,n||m){
while(scanf("%d %d",&n,&m),n||m){//memory较小 time差不多
vector<int> cost(m+1);//全部+1!!!
vector<double> p_s(m+1);//double
vector<double> p_f(m+1,1);
vector<double> minp_f(n+1,1);
for(int i=1;i<=m;++i){
//cin>>cost[i]>>p_s[i];
scanf("%d %lf",&cost[i],&p_s[i]);
p_f[i]-=p_s[i];
}
//两重循环!不能调换内外循环次序 否则将利用到i=m的minp_f[j]值
for(int i=1;i<=m;++i){
for(int j=n;;--j){//j 逆序!i和i-1存储在同一个minp_f[j]中 i会将i-1覆盖
if(j<cost[i])break;//将for循环中的条件省略 这样写有时快一些
minp_f[j]=min(minp_f[j],minp_f[j-cost[i]]*p_f[i]);
}
}
//cout<<(1-minp_f[n])*100<<"%"<<endl;
printf("%.1lf%%\n",(1-minp_f[n])*100);
}
return 0;
}
hdoj 1003
DP 最大的子序列累计和,外加子序列头尾所在的位置
sum记录当前的累计和,maxv从全局考虑
len.......................序列长,si,ei......................
搭配着修改
//hdoj 1003 最大的子序列累计和
#include <vector>
#include <stdio.h>
using namespace std;
int main(){
int t;
scanf("%d",&t);
for(int i=0;i<t;++i){
if(i>0) printf("\n");/////otherwise PE
int n;
scanf("%d",&n);
vector<long long> v(n);
for(int j=0;j<n;++j){
scanf("%lld",&v[j]);
}
int si=0;
int ei=0;
if(n==1){
printf("Case %d:\n",i+1);
printf("%lld %d %d\n",v[0],si+1,ei+1);
continue;
}
long long sum=v[0];
long long maxv=v[0];
int len=1;////
for(int j=1;j<n;++j){
if(sum>=0){
sum+=v[j];//记录包含v[j]的子序列的累计和
++len;///未必是最大累积和子序列中的部分(未必参与si的计算)
///但是当前序列还有后续 最后也有可能在最大累积和子序列中
///所以要另设len进行记录
}
else{
sum=v[j];//前面的部分为负 对包含v[j]的序列累计和只有负贡献 因此重新累加
len=1;////
}
if(sum>maxv){///只要sum改变 就有可能超过maxv 上面if else中都更新了sum
maxv=sum;
ei=j;
si=ei-len+1;///maxv更新时同步更新ei和si 而不是在最后才更新
}
}
printf("Case %d:\n",i+1);
printf("%lld %d %d\n",maxv,si+1,ei+1);//
}
return 0;
}