求助:杭电ACM 2089

AAA20090987 2009-09-29 10:37:36
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2089

这是我写的程序:
#include <iostream>
using namespace std;

bool Have(int n);

int main()
{
int n, m, i, result;
while(cin >> n >> m)
{
if(0==n && 0== m)
break;
result = 0;
for(i=n;i<=m;i++)
if(Have(i))
result++;
cout << result << endl;
}
return 0;
}

bool Have(int n)
{
while(n > 0)
{
if(0==(n-62)%100 || 0==(n-4)%10)
return false;
n /= 10;
}
return true;
}

但交上去却显示:“Time Limit Exceeded”,请问大家有什么好的思路吗?
小弟是ACM的新手,请不要见笑,谢谢。
...全文
880 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
myxiaodiaodiao 2012-10-08
  • 打赏
  • 举报
回复
我感觉这些题目好难啊。但是我们老师直接拿来给我们做
「已注销」 2009-10-05
  • 打赏
  • 举报
回复
呵呵...受益不浅,谢谢各位啦!
karlfixed 2009-10-05
  • 打赏
  • 举报
回复
这方法强,MARK
wubaozhang 2009-10-01
  • 打赏
  • 举报
回复
大家算法都是C的,兄弟混的ASP惭愧啊看不大懂大家的高论
wubaozhang 2009-10-01
  • 打赏
  • 举报
回复
这样不知道在效率上能不能达到要求:
m=10033
n=630001

Num=0
while m <=n

'response.write "xxxxx"&cStr(m)&"xxxxx<br>"

strM=cStr(m)
if instr(strM,"4")=0 then
if inStr(strM,"62")=0 then
Num=Num+1
strM=Replace(strM,"4","XXXXXXXXXX4XXXXXXXXXX")
strM=Replace(strM,"62","XXXXXXXXXX62XXXXXXXXXX")
'response.write strM&"<br>"
m=m+1
else
m=m+10^(len(strM)-InStrRev(strM,"62")-1)
'response.write "000000000000000000000000000000000000000000<br>"
end if
else
m=m+10^(len(strM)-InStrRev(strM,"4"))
'response.write "000000000000000000000000000000000000000000<br>"
end if
wend

精简了在极端情况下如最上位3进位5时多余计算,我想应该是足够了,反正我这里10000个号码不需要1秒就可以算出来了

另外还有一种时间换空间的算法的延续:
记录1 2 3 4 5 6 7 8 9 中排除数的个数
记录10 20 30 40 50 60 70 80 90 中排除数的个数
记录100 200 300 400 500 600 700 800 900 中排除数的个数
记录1000 2000 3000 4000 5000 6000 7000 8000 9000中排除数的个数
............
一直记录到符合要求的位数

然后根据给出的数字每一位上对应的数字对应得到
如计算633567时,结果为
600000对应数+30000上对应数+3000上对应数+500上对应数+60上对应数+7上对应个数
因为无须考虑给出的车牌本身就有问题如要求计算6234567上62和4带来的问题,所以我认为这种算法也是可行的
如果考虑到给出的车牌本身就有问题,只需要在开始计算的时候就做个判断取下限后最小合理数和上限前最大合理数就可以了
fanster28_ 2009-10-01
  • 打赏
  • 举报
回复
所以都是o(lg(n))的算法。O(1)怕是办不到了。
showjim 2009-09-30
  • 打赏
  • 举报
回复
上面的有点错误
public static int lucky(int start, int end)
{
int value = 0;
if (start <= end)
{
if (start <= 0) start = 1;
if (end >= 1000000) end = 999999;
int[] c2 = { 1, 0, 0, 0, 0 }, c4 = { 1, 0, 0, 0, 0 }, c = { 8, 0, 0, 0, 0 };
for (int j, sum = 100, i = 1; i < 5; sum *= 10, i++) c[i] = sum - (c2[i] = c2[j = i - 1] + c[j]) - (c4[i] = (c2[j] << 1) + 10 * c4[j] + c[j]);
value = lucky(end, c2, c) - (start == 1 ? 0 : lucky(start - 1, c2, c));
}
return value;
}
private static int lucky(int end, int[] count2, int[] count)
{
int value = 0;
List<int> mod = new List<int>();
while (end > 9)
{
mod.Add(end % 10);
end /= 10;
}
mod.Add(end);
int m, lm = 0;
bool not62 = true;
for (int c, ci, i = mod.Count - 1; not62 && i > 0; not62 = m != 4 && (m != 2 || lm != 6), lm = m, i--)
{
if ((m = mod[i]) != 0)
{
value += m * (c = count2[ci = i - 1] + count[ci]);
if (lm == 6 && m > 2) value -= c;
if (m > 4)
{
value -= c;
if (m > 6) value -= count2[ci];
}
}
}
if (not62)
{
value += (m = mod[0]);
if (m < 4)
{
if (lm != 6 || m < 2) value++;
}
else if (lm == 6) value--;
}
return value - 1;
}
showjim 2009-09-30
  • 打赏
  • 举报
回复
public static int lucky(int start, int end)
{
int value = 0;
if (start <= end)
{
if (start <= 0) start = 1;
if (end >= 1000000) end = 999999;
int[] c2 = { 1, 0, 0, 0, 0 }, c4 = { 1, 0, 0, 0, 0 }, c = { 8, 0, 0, 0, 0 };
for (int j, sum = 100, i = 1; i < 5; sum *= 10, i++) c[i] = sum - (c2[i] = c2[j = i - 1] + c[j]) - (c4[i] = (c2[j] << 1) + 10 * c4[j] + c[j]);
value = lucky(end, c2, c) - (start == 1 ? 0 : lucky(start - 1, c2, c)) - 1;
}
return value;
}
private static int lucky(int end, int[] count2, int[] count)
{
int value = 0;
List<int> mod = new List<int>();
while (end > 9)
{
mod.Add(end % 10);
end /= 10;
}
mod.Add(end);
int m, lm = 0;
bool not62 = true;
for (int c, ci, i = mod.Count - 1; not62 && i > 0; not62 = m != 4 && (m != 2 || lm != 6), lm = m, i--)
{
if ((m = mod[i]) != 0)
{
value += m * (c = count2[ci = i - 1] + count[ci]);
if (lm == 6 && m > 2) value -= c;
if (m > 4)
{
value -= c;
if (m > 6) value -= count2[ci];
}
}
}
if (not62)
{
value += (m = mod[0]);
if (m < 4)
{
if (lm != 6 || m < 2) value++;
}
else if (lm == 6) value--;
}
return value;
}
AAA20090987 2009-09-30
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 wubaozhang 的回复:]
有你们这么麻烦的吗,炫耀技巧啊
整个深圳市一年想新增2500台的士都引起骚动,你们以为杭州1天能新增多少台?
C++我不熟,用VBs表示好了,区间为M到N:

Num=0
while m <=n
strM=cstring(m)
if instr(strM,"4")=0 then
if instr(strM,"62")=0 then
Num=Num+1
end if
end if
next
m=m+1
next

Num即为所求
[/Quote]

这样是很简单,也能求出结果,但交上去会“Time Limit Exceeded”。
我试过这种方法了。
fire_woods 2009-09-30
  • 打赏
  • 举报
回复
性能和复杂度有时候不能兼顾.

这个题目就像是求1-n的和
循环累加简单,但是慢.
用公式n*(n+1)/2快,但是需要知道等差数列的公式.
就这么回事情.

所以要想代码完成快,就循环解决,要想效率高,就应该先推导出数学公式.
绿色夹克衫 2009-09-30
  • 打赏
  • 举报
回复
如果只是4的话,可以直接用9进制,加上62之后,稍微复杂一点,用Dp推导就可以了,2开头的前面不能加6,
大概就是这样了!
fanster28_ 2009-09-30
  • 打赏
  • 举报
回复
其实是利用了n不超过6位的,就是o(log(n))了,跟描述的一样。
fanster28_ 2009-09-30
  • 打赏
  • 举报
回复
对 for (int i=0;i<=5;i++){
d[i] = (n % 10);
n /= 10;
}
我觉得就不算0(1)了
Tiger_Zhao 2009-09-30
  • 打赏
  • 举报
回复
长久不用 C 了,大致意思补完整一下,O(1) 的算法
#include <iostream> 
using namespace std;

int a[] = {1,9,80,711,6319,56160};

int f(int m, int n){
return (fx(n)-f(m-1));
}
int fx(int n){
byte d[7];
for (int i=0;i<=5;i++){
d[i] = (n % 10);
n /= 10;
}
return fy(d);
}
int fy(byte d[]){
...
}

int main()
{
int n, m, result;
while(cin >> n >> m)
{
if(0==n && 0== m)
break;
result = f(m,n);
cout < < result < < endl;
}
return 0;
}
Tiger_Zhao 2009-09-30
  • 打赏
  • 举报
回复
f(m,n) 表示求区间 [m,n] 之间的不含有不吉利数字的统计个数,有
f(m,n) = f(0,n) - f(0,m-1)

用 fx(n) 表示 f(0,n),针对特殊的 n 为 {9,99,999,9999,...} 序列有 a[k+1] = 9a[k] - a[k-1](21楼),这个序列结果为
a[] = {1,9,80,711,6319,56160} //a[0]=1 不对应具体数字,方便计算。

用 fy(d[]) 来计算 fx(n),d[] 为数字 n 的各位十进制数字,比如 n=36574 则 d={4,7,5,6,3,0,0}
这样可以将区间 [0,36574] 拆成多个区间进行统计

//★ d[4] = 3
+ a(4)*3 //[00000, 29999]
//★ d[3] = 6
+ a(3)*5 //[30000,33999]∪[35000,35999]
//★ d[2] = 5
+ a(2)*4 //[36000,36399]
- a(2) //排除 [36200,36299]
//★ d[1] = 7
+ a(1)*6 //[36500,36539]∪[36550,36569]
- a(0) //排除 [36562,36562]
//★ d[0] = 4
+ a(0)*4 //[36570,36573]
---------
= 22809

int fy(d[]){
d[k]++; //个位不向下拆分,要算上自己

count=0;
for(k=5;k>0;k--){
//比如 d[3]==7,统计 [##0000, ##6999 ] 区间
if (d[k]<=4){
count += a[k] * d[k];
}else{
count += a[k] * (d[k]-1); //排除##4###
}

//排除 #62###
if ((d[k+1]=6) && (d[k]>2))
count -= a[k];

//排除 ##62##
if ((d[k]>6) && (k>0))
count -= a[k-1];
}

return count;
}
AAA20090987 2009-09-30
  • 打赏
  • 举报
回复
23楼太强大了,佩服佩服。
小弟自愧不如啊,至今还只会做一些杭电ACM的水题。
fanster28_ 2009-09-30
  • 打赏
  • 举报
回复

#include <string.h>
#include <stdio.h>

int b[7] = {1, 9, 80, 711, 6319, 56160, 499121};
int g(char *a);
int f(char *a)
{
int len;
len = strlen(a);
if (len == 0)
return 0;
else if (len == 1)
{
if (a[0] >= '4')
return a[0] - '0';
else
return a[0] - '0' + 1;
}
else
{
switch(a[0]-'0')
{
case 0:
case 1:
case 2:
case 3: return f(a+1) + (a[0] - '0')*b[len-1];
case 4: return 4*b[len-1];
case 5: return 4*b[len-1] + f(a+1);
case 6: return 5*b[len-1] + f(a+1) - g(a+1);
default: return (a[0] - '0' - 1)*b[len-1] + f(a+1) - b[len - 2];
}
}
}

int g(char *a)
{
if (a[0] == '\0')
return 0;
switch(a[0] - '0')
{
case 0:
case 1: return 0;
case 2: return (a[1] == '\0')?1:f(a+1);
default: return b[strlen(a) - 1];
}
}

int main()
{
int m, n;
int k, t;
char a[10];
while (scanf("%d %d", &m, &n) != EOF)
{
if (m == 0 && n == 0)
break;
if (0 == m)
a[0] = '\0';
else
sprintf(a, "%d", m-1);
k = f(a);

sprintf(a, "%d", n);
t = f(a);
printf("%d\n", t - k);
}
}


如上所诉的算法c代码,0ms, 感觉应该有0(1)的算法才对。。。
fire_woods 2009-09-30
  • 打赏
  • 举报
回复
如果结果对的话,写出来的算法应该只需要0ms.
fanster28_ 2009-09-30
  • 打赏
  • 举报
回复
其实还是有规律的

按位数考虑
0 ~ 9 满足条件的有 9 个
0 ~ 99 满足条件的有 80 个
0 ~ 999 满足条件的有 712 个

于是得到递归公式 a[k+1] = 9a[k] - a[k-1] 此数列可求出通项表达式的

于是 n 对应为bk, bk-1, ... , b1

用f(bkbk-1...b1) 表示0 ~ n中满足的个数
则对bk考虑
switch(bk){
case 1:
case 2:
case 3:f(bkbk-1...b1) = bk*a[k-1] + f(bk-1bk-1...b1); break;
case 4:f(bkbk-1...b1) = 4*a[k-1]; break;
case 5:f(bkbk-1...b1) = 4*a[k-1] + f(bk-1bk-1...b1); break;
case 6:f(bkbk-1...b1) = 5*a[k-1] + f(bk-1bk-1...b1) - g(bk-1bk-1...b1); break; //同样减去2开头的?
case 7:
case 8:
case 9:f(bkbk-1...b1) = (bk - 1)*a[k-1] + f(bk-1bk-1...b1) - a[k-2]; break;//最后一项表示的是6开头时减去2开头的
}

g(bkbk-1...b1)=
switch(bk){
case 1: return 0;
case 2: return f(bk-1bk-2...b1)
default: return a[k-1];
}

无奈啊, 谁给个O(1)的算法
fanster28_ 2009-09-30
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 linren 的回复:]
【程序】
C/C++ code#include<stdio.h>
#include<string.h>int fun(int x,int*c){int i,len,f,n;char a[10];

sprintf(a,"%d",x);
len=strlen(a);

f=0;*c=1;for(i=0;i<len;i++){if(a[i]=='6'){if(i+1<len&&a[i+1]=='2'){
f=2;break;
}
}elseif(a[i]=='4'){
f=1;break;
}
}if(f==2){
a[++i]='1';*c=0;for(i=i+1;i<len;i++) a[i]='9';
}elseif(f==1){
a[i]='3';*c=0;for(i=i+1;i<len;i++) a[i]='9';
}for(i=0;i<len;i++){if(a[i]>='5') a[i]=a[i]-1;
}
n=a[0]-'0';for(i=1;i<len;i++){
n=n*9+a[i]-'0';
}return n;
}int data[531442]={0};int have(int n){while(n>0){if(0==(n-47)%81)return0;
n/=9;
}return1;
}int main(){int i,k;int m,n;int a,b,c;

k=0;for(i=0;i<531442;i++){
k+=have(i);
data[i]=k;
}while(scanf("%d %d",&m,&n)!=EOF){if(m==0&&n==0)break;
b=fun(n,&c);
a=fun(m,&c);
printf("%d\n",data[b]-data[a]+c);
}return0;
}
【提交结果】
Assembly code17182932009-09-2916:24:29 Accepted2089 46MS2292K945 B C linren17182812009-09-2916:20:46 Accepted2089 46MS2292K960 B C linren17182662009-09-2916:15:56 Accepted2089 31MS2292K1202 B C linren17182612009-09-2916:13:34 Accepted2089 46MS2292K1272 B C linren17182522009-09-2916:10:10 Accepted2089 46MS2292K1318 B C linren17179892009-09-2913:58:34 Time Limit Exceeded2089 1000MS188K1238 B C linren

看到了一个非常厉害的:
Assembly code15609182009-08-0221:11:03 Accepted2089 0MS1220K 534B C++ Heller→付海镠
只用了0毫秒……
[/Quote]

linren解释一下, 没看明白, 尤其是那个have(n)
加载更多回复(15)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧