菜鸡蜕变从今天开始。昨天成哥在群里发了道题,佳哥说用区间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的情况;
题外话:
还get了一个新方法判断回文数 无须转化为字符数组
思路:
若一个数为回文数,则其倒过来依然为原来的数;
小数据时用这个 数字大(10^100)或者是字符串是就还是用字符数组判断;
不过上述题目最终还是得回到区间dp上,继续。
话不多说 贴代码。
#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号,哈哈顺便祝自己生日快乐。
=======================
记录自己的学习生活,排版哈哈,若有幸被大佬看见,见谅见谅,有问题欢迎指正