hdoj水题练习(六)

emmm,这个系列似乎没那么水了><

DP系列,虽然个人觉得第一题没那么典型,不过确实也用到了自底向上的递推。


hdoj 2062

思路:递推求解每个分组中数的个数

注意:1、long long 和 %lld(Runtime Error(ACCESS_VIOLATION) or WA...

          2、字典序

          3、m要减去空集的情况

//hdoj 2062
#include 
//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


hdoj 1087

DP    空间换时间

//hdoj 1087 求序列中最大的递增子序列累计和
#include 
#include////max,max_element
#include
//#include 
//#include 
using namespace std;
int main(){
	int n;//31MS 1888K
	while(scanf("%d",&n),n){
		vector v(n);
		for(int i=0;i maxv(n,0);
		vector sum(n,0);
		vector tail(n,0);
		sum[0]+=v[0];
		maxv[0]=max(sum[0],maxv[0]);
		tail[0]=v[0];
		for(int i=1;i=0;--j){//遍历递减序列(46MS 1916K)//long long n;vector 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 
#include
#include
#include ///设置格式
#include //格式输出更方便
using namespace std;
int main(){
	int n,m;
	//cout<>n>>m,n||m){
	while(scanf("%d %d",&n,&m),n||m){//memory较小 time差不多
		vector cost(m+1);//全部+1!!!
		vector p_s(m+1);//double
		vector p_f(m+1,1);
		vector 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

hdoj 1003

DP    最大的子序列累计和,外加子序列头尾所在的位置

sum记录当前的累计和,maxv从全局考虑

len.......................序列长,si,ei......................

搭配着修改

//hdoj 1003 最大的子序列累计和
#include 
#include 
using namespace std;
int main(){
	int t;
	scanf("%d",&t);
	for(int i=0;i0) printf("\n");/////otherwise PE
		int n;
		scanf("%d",&n);
		vector v(n);
		for(int j=0;j=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;
}


你可能感兴趣的:(oj水题)