hdoj水题练习(六)

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    空间换时间

//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;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值