20171216区间dp(有错请各位大佬指正)

菜鸡蜕变从今天开始。昨天成哥在群里发了道题,佳哥说用区间dp做。我先尝试暴力。=================  
 暴力时间太长 达到规定的10^9数量级辅助程序消耗30分钟,且输出文件不知名损坏(??!!)  
 区间dp     区间动态规划问题一般是考虑,对于每段区间,他们的最优值都是由几段更小的区间的最优值得到,是分治思想的一种应用,讲一个区间   的问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合,求合并后的最优值。 
 ----来自csdn某博主。     设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价       F[i,i]=0(一个数字无法合并,so 代价为0)       每次用变量k(i<=k<=j-1)将区间分为[i,j],[k+1,j]两段 
 for (l=1;l<=n;l++)//l是区间长度,作为阶段(阶段是个什么玩意儿??!!) 
 for (i=1;i<=n;i++)//i是枚举的区间起点 
 { 
 j=i+l-1; 
 if (j>n) break;//划重点!!不要漏写 
 for (k=i,k<=j-1;k++) 
 F[i,j]=max{f[i,k],f[k+1,j]+w[i,j]};//状态转移方程 
 }???!!讲完了1+1 那我现在就去算积分咯??!!蛇皮操作好 我复述一遍昨天的题//不然现在无话可说岂不是很尴尬    找出回文数    输入输出要求  
  第一行输入一个整数t(t<20),表示一共t组数据; 
 第二行一个整数n 表示询问n个区间;n<10^6; 
 接下来n行,每行两个整数,l,r表示区间起点终点;l<r<10^9 
 对于每组每个区间 输出区间内的回文整数个数哈哈哈哈或  我晓得咯 //终于明白大佬的思路  首先初始化一个f数组 f[i]表示1—10^(i-1)里回文数的个数//写另一个程序找出这些数据。  那么一个数如abcd  1—abcd的回文数为f(3)+(大于1000)的 
 大于1000部分   将abcd分为ab,cd两部分,显然在1—ab-1这个区间的数都能有回文数,      而ab这个数能否构成回文数就看(cd>ba)是否成立,注意是ba!!      若为abcde类型 则判断cde>cba      设pali[i]表示1—i内回文数个数,那么pali[i]=f[i位数-1]+(前一半构成的数-10^位数/2-1)+判断(是否加一)//即pali[1238]=f[3]+(12-10)+((38>21)?1:0); 
 最终a~b内则为pali[b]-pali[a-1];----------来自csdn某博主  写代码写代码。  第一段代码采用先倒着存,然后写一个rev函数将其倒回来。  这样就存在一个问题,比如若一个数为1031,倒着存,q1=1,h=13;调用rev函数时,q=rev(q1)==1,而本身q应该=10;  看了大佬代码后明白 可以采用这样的方法 
 将l记为位数,w记为位数/2; 
 定义变量t为10^w;//一个循环搞定 
 那么q=n/t;h=n%t;//若l为奇数则t*10  t/10一次操作就行。 
 这样q1再调用rev函数就不会出现少掉0的情况; 
 

话不多说 贴代码。

#include<iostream>
using namespace  std;
const long f[12]={0,9,18,108,198,1098,1998,10998,19998,109998,199998,1099998};
int rev(int n)
{
	int k=0;
	while (n)
	{
		k=k*10+n%10;
		n/=10;
	}
	return k;
}
int ws(int n)
{
	int k=0;
	while(n)
	{
		k++;
		n/=10;
	}
	return k;
}
long dp(int n)
{
	int i,j,q=0,h=0,l,q1=0,ans;//q1反存前面的数,q正存前面的数,h正存后面的数 
	int k=n;
	if (n<10) return n;
	int m=ws(k)>>1;//位数/2 
	l=ws(k);//位数
	int t=1;
	for (i=1;i<=m;i++)
	  t*=10;
	ans=f[l-1];
	if (l%2==0)
	{
		q=k/t;
		h=k%t;
		t/=10;
		ans+=q-t;
		q1=0;
		while(q)
		{
			q1=q1*10+q%10;
			q/=10;
		}
	}
	else
	{
		q=k/t;
		t*=10;
		h=k%t;
		ans+=q-t/10;
		q1=0;
		while(q)
		{
			q1=q1*10+q%10;
			q/=10;
		}
	}
	if (h>=q1) ans++;
	return ans;
}
//	if (l%2==0)
//	{
//		while(k)
//		{
//			if (m)
//			{
//		    	h=h*10+k%10;
//		    	m--;
//		    }
//		    else 
//		    {
//		      q1=q1*10+k%10;
//		      m--;
//		  }
//		    k/=10;
//		}
//	}
//	else
//	{
//		m++;
//		while(k)
//		{
//			if(m>1)
//			{
//			  h=h*10+k%10;
//			  m--;
////			  continue;
//		    }
//			else if(m==1)
//			{
//				h=h*10+k%10;
//				q1=q1*10+k%10;
//				m--;
//			}
//			else
//			{
//				q1=q1*10+k%10;
//			}
//			k/=10;
//			  
//		}
//	}
//	q=rev(q1);
//	h=rev(h);
//	cout<<q<<" "<<q1<<" "<<h<<endl;
//	return ((f[l-1]+q-1)+((h>=q1)?1:0));
//}
int main()
{
	int t,i,j,k,p,l,r,n,x;
	cin>>n;
	while(n--)
	{
//		cin>>x;
//		cout<<dp(x)<<endl;
		cin>>l>>r;
		cout<<dp(r)-dp(l-1)<<endl;
	}
 } 







题外话:
还get了一个新方法判断回文数 无须转化为字符数组
思路:
若一个数为回文数,则其倒过来依然为原来的数;

设计算法实现将一个数倒过来:

			for (int m=n;m>0;m/=10)
	{
		t=t*10+m%10;
		k+=m%10;
	}
	if (t==n) return true;

按数位计算 时间很少!注意赋初始值

相对于之前的暴力(小数据用了字符数组判断的方法,杀鸡用牛刀,不宜不宜)可能要近半个小时 这个算法相同数量级只需不到一分钟,也算进步了。
小数据时用这个  数字大(10^100)或者是字符串是就还是用字符数组判断;
不过上述题目最终还是得回到区间dp上,继续。

一个16号的题 拖到了17号,哈哈顺便祝自己生日快乐。

=======================

   记录自己的学习生活,排版哈哈,若有幸被大佬看见,见谅见谅,有问题欢迎指正



  

猜你喜欢

转载自blog.csdn.net/qq_40241747/article/details/78823894