此博客关闭

转移到tjbwyk.wordpress.com
如果需要,请自备翻人比黄花瘦

Leave a comment

高精度加减乘除法(不压位及压位)

今天凌晨3:30,终于实现了我学习OI一来的一个夙愿:写高精度除法,并较好的实现了压位操作以及与之配套的输入输出操作。贴代码纪念一下:
1、无压位操作的
[code=cpp]
#include
#include

/*
大整数储存方式:
1、a[0]存位数
2、a[1]、a[2]……依次存个位、十位……(倒序,个位在a[1]对齐)

这样存的话,中间关于位置的计算全都不用带MAXL,个位对齐方便
*/

#define MAXL 500

void Hi_Add(int a[], int b[], int c[]);//加法
void Hi_Sub(int a[], int b[], int c[]);//减法
void Hi_Mul(int a[], int b[], int c[]);//乘法
void Hi_Div(int a[], int b[], int c[]);//除法,感觉还是不太容易直接调用Hi_Sub函数,所以给除法单独写了个Hi_Div_Sub减法函数
int Hi_Div_Cmp(int a[], int b[]);//供除法调用的比较函数
void Hi_Div_Sub(int a[], int b[]);//供除法调用的减法函数,指针很容易实现对位
void print(int a[]);//打印大整数
void swap(int *a, int *b);

int a[MAXL]={0},b[MAXL]={0},c[MAXL]={0};
char operater;

void init()//这是读入部分,把表达式拆开,格式和C++有点不一样,领会意思就行了
{
char ch;
int i;
while (scanf("%c",&ch),ch>='0'&&ch<='9') a[++a[0]]=ch-'0'; operater=ch; while (scanf("%c",&ch),ch>='0'&&ch<='9') b[++b[0]]=ch-'0'; for (i=1; i<=a[0]/2; i++) swap(a+i,a+a[0]+1-i); for (i=1; i<=b[0]/2; i++) swap(b+i,b+b[0]+1-i); } void doit() { print(a); printf("%c",operater); print(b); printf("="); switch (operater) { case '+': Hi_Add(a,b,c); print(c); printf("\n"); break; case '-': Hi_Sub(a,b,c); print(c); printf("\n"); break; case '*': Hi_Mul(a,b,c); print(c); printf("\n"); break; case '/': Hi_Div(a,b,c);//经过除法操作后,a数组剩下的刚好是余数(详见Hi_Div函数定义) print(c); printf("……");//顺便加个余数显示 print(a); printf("\n"); break; } } int main() { init(); doit(); return 0; } void Hi_Add(int a[], int b[], int c[]) { int i; memset(c,0,sizeof(c)); c[0]=a[0]>b[0] ? a[0]:b[0];//c位数初始化
for (i=1; i<=c[0]; i++) { c[i]+=a[i]+b[i]; c[i+1]+=c[i]/10; c[i]%=10; } if (c[c[0]+1]) c[0]++;//如最高位有进位 } void Hi_Sub(int a[], int b[], int c[]) { int i; memset(c,0,sizeof(c)); c[0]=a[0]; for (i=1; i<=c[0]; i++) { c[i]+=a[i]-b[i]; if (c[i]<0) { c[i]+=10; c[i+1]--; } } while (c[c[0]]==0&&c[0]>1) c[0]--;//去掉高位的0,注意,如果结果是0,个位的0要保留(位数至少为1,下同)
}

void Hi_Mul(int a[], int b[], int c[])
{
int i,j;
memset(c,0,sizeof(c));
c[0]=a[0]+b[0]-1;
for (i=1; i<=a[0]; i++) for (j=1; j<=b[0]; j++) { c[i+j-1]+=a[i]*b[j]; c[i+j]+=c[i+j-1]/10; c[i+j-1]%=10; } while (c[c[0]+1]!=0)//处理最高位后的进位 { c[0]++; c[c[0]+1]=c[c[0]]/10; c[c[0]]%=10; } while (c[c[0]]==0&&c[0]>1) c[0]--;//万一有个乘数是0的情况,位数会减少,去0
}

void Hi_Div(int a[], int b[], int c[])
{
int i;
memset(c,0,sizeof(c));
c[0]=a[0]-b[0]+1;//商最高位不超过a[0]-b[0]+1,c位数初始化一下
for (i=a[0]-b[0]+1; i>0; i--)
while (Hi_Div_Cmp(a+i-1,b)>=0)//用指针,对齐待减的部分和b的个位(联想竖式除法)
{ //其实可以完全不用为试除作乘法,在当前位一直减就可以了,直到减不动为止(时间效率理论上比乘法试除要高)
Hi_Div_Sub(a+i-1,b); //每对a减一次,就在c的对应位+1(a中不断地减去b的倍数,最后剩下的就是余数)
c[i]++;
}
while (c[c[0]]==0&&c[0]>1) c[0]--;
while (a[a[0]]==0&&a[0]>1) a[0]--;//剩下的a作为余数,化为标准大整数形式,以备输出
}

int Hi_Div_Cmp(int a[], int b[])//比较函数,看是否还能再做减法
{
int i;
for (i=b[0]+1; i>0; i--)
{
if (a[i]>b[i]) return 1;
if (a[i]<=b[0]; i++) { a[i]-=b[i]; if (a[i]<0) { a[i]+=10; a[i+1]--; } } } void print(int a[])//打印大整数 { int i; for (i=a[0]; i>0; i--) printf("%i",a[i]);
}

void swap(int *a, int *b)
{
int c;
c=*a;
*a=*b;
*b=c;
}
[/code]

2、经过压位操作的
[code=cpp]
//这是经过压位的大整数计算,比较一下,其实主过程和普通几乎一样
//不一样的地方:1、所有跟10进制有关的地方都改成UPPERBOUND控制(可以改成10,100,10000...),同时对应的位数DIGITS也要改
// 2、打印大整数部分会有些不同
// 3、读入时处理复杂一些
// 4、优点:加减乘运算减少运算次数,压位后空间利用率变高
// 5、缺点:读入和输出稍复杂,除法每次作差试除效率变低

#include
#include

/*
大整数储存方式:
1、a[0]存位数
2、a[1]、a[2]……依次存个位、十位……(倒序,个位在a[1]对齐)

这样存的话,中间关于位置的计算全都不用带MAXL,个位对齐方便
*/

#define MAXL 500
#define UPPERBOUND 10000
#define DIGITS 4

void Hi_Add(int a[], int b[], int c[]);//加法
void Hi_Sub(int a[], int b[], int c[]);//减法
void Hi_Mul(int a[], int b[], int c[]);//乘法
void Hi_Div(int a[], int b[], int c[]);//除法,感觉还是不太容易直接调用Hi_Sub函数,所以给除法单独写了个Hi_Div_Sub减法函数
int Hi_Div_Cmp(int a[], int b[]);//供除法调用的比较函数
void Hi_Div_Sub(int a[], int b[]);//供除法调用的减法函数,指针很容易实现对位
void print(int a[]);//打印大整数

int a[MAXL]={0},b[MAXL]={0},c[MAXL]={0};
char operater;

void init()//这是读入部分,把表达式拆开,格式和C++有点不一样,领会意思就行了。有兴趣可以认真读一下。
{
int temp[MAXL]={0},i,mask;
char ch;

while (scanf("%c",&ch),ch>='0'&&ch<='9') temp[++temp[0]]=ch-'0'; operater=ch; for (i=1; i<=temp[0]; i++) { if (i%DIGITS==1) mask=1; else mask*=10; a[(i+DIGITS-1)/DIGITS]=a[(i+DIGITS-1)/DIGITS]+=temp[temp[0]+1-i]*mask; } a[0]=(temp[0]+DIGITS-1)/DIGITS; memset(temp,0,sizeof(temp)); while (scanf("%c",&ch),ch>='0'&&ch<='9') temp[++temp[0]]=ch-'0'; for (i=1; i<=temp[0]; i++) { if (i%DIGITS==1) mask=1; else mask*=10; b[(i+DIGITS-1)/DIGITS]=b[(i+DIGITS-1)/DIGITS]+=temp[temp[0]+1-i]*mask; } b[0]=(temp[0]+DIGITS-1)/DIGITS; } void doit() { print(a); printf("%c",operater); print(b); printf("="); switch (operater) { case '+': Hi_Add(a,b,c); print(c); printf("\n"); break; case '-': Hi_Sub(a,b,c); print(c); printf("\n"); break; case '*': Hi_Mul(a,b,c); print(c); printf("\n"); break; case '/': Hi_Div(a,b,c);//经过除法操作后,a数组剩下的刚好是余数(详见Hi_Div函数定义) print(c); printf("……");//顺便加个余数显示 print(a); printf("\n"); break; } } int main() { init(); doit(); return 0; } void Hi_Add(int a[], int b[], int c[]) { int i; memset(c,0,sizeof(c)); c[0]=a[0]>b[0] ? a[0]:b[0];//c位数初始化
for (i=1; i<=c[0]; i++) { c[i]+=a[i]+b[i]; c[i+1]+=c[i]/UPPERBOUND; c[i]%=UPPERBOUND; } if (c[c[0]+1]) c[0]++;//如最高位有进位 } void Hi_Sub(int a[], int b[], int c[]) { int i; memset(c,0,sizeof(c)); c[0]=a[0]; for (i=1; i<=c[0]; i++) { c[i]+=a[i]-b[i]; if (c[i]<0) { c[i]+=UPPERBOUND; c[i+1]--; } } while (c[c[0]]==0&&c[0]>1) c[0]--;//去掉高位的0,注意,如果结果是0,个位的0要保留(位数至少为1,下同)
}

void Hi_Mul(int a[], int b[], int c[])
{
int i,j;
memset(c,0,sizeof(c));
c[0]=a[0]+b[0]-1;
for (i=1; i<=a[0]; i++) for (j=1; j<=b[0]; j++) { c[i+j-1]+=a[i]*b[j]; c[i+j]+=c[i+j-1]/UPPERBOUND; c[i+j-1]%=UPPERBOUND; } while (c[c[0]+1]!=0)//处理最高位后的进位 { c[0]++; c[c[0]+1]=c[c[0]]/UPPERBOUND; c[c[0]]%=UPPERBOUND; } while (c[c[0]]==0&&c[0]>1) c[0]--;//万一有个乘数是0的情况,位数会减少,去0
}

void Hi_Div(int a[], int b[], int c[])
{
int i;
memset(c,0,sizeof(c));
c[0]=a[0]-b[0]+1;//商最高位不超过a[0]-b[0]+1,c位数初始化一下
for (i=a[0]-b[0]+1; i>0; i--)
while (Hi_Div_Cmp(a+i-1,b)>=0)//用指针,对齐待减的部分和b的个位(联想竖式除法)
{ //其实可以完全不用为试除作乘法,在当前位一直减就可以了,直到减不动为止(时间效率理论上比乘法试除要高)
Hi_Div_Sub(a+i-1,b); //每对a减一次,就在c的对应位+1(a中不断地减去b的倍数,最后剩下的就是余数)
c[i]++;
}
while (c[c[0]]==0&&c[0]>1) c[0]--;
while (a[a[0]]==0&&a[0]>1) a[0]--;//剩下的a作为余数,化为标准大整数形式,以备输出
}

int Hi_Div_Cmp(int a[], int b[])//比较函数,看是否还能再做减法
{
int i;
for (i=b[0]+1; i>0; i--)
{
if (a[i]>b[i]) return 1;
if (a[i]<=b[0]; i++) { a[i]-=b[i]; if (a[i]<0) { a[i]+=UPPERBOUND; a[i+1]--; } } } void print(int a[])//打印大整数 { int i,mask=UPPERBOUND/10,x; for (i=a[0]; i>0; i--)
if (i==a[0]) printf("%i",a[i]);
else //中间部分高位出现0,必须用0补齐
{
x=a[i];
while (mask>0)
{
printf("%i",x/mask);
x%=mask;
mask/=10;
}
}
}
[/code]

Tagged , | 2 Comments

POJ 1011 Sticks (Central Europe 1995)解题报告

现在搜索剪枝优化能力明显不如当年了。这么一道以前做过的题,不是搜索策略出错WA,就是优化不到家TLE。好不容易翻出以前的Pascal程序,翻译成了C,勉强AC了。

1、首先,预处理用背包算出所有可能达到的长度;

2、用计数排序优化(因为长度<=50,木棍数<=64,枚举长度比枚举木棍代价低?);

3、类似迭代加深搜索,枚举原始长度进行判断;

4、对于每根原始木棍,必选当前最短没被用到的木棍,枚举其余木棍按长度升序枚举,每组成一根原始木棍就不再改变已使用的木棍的状态。

这些对于测试数据足矣,不过对于传说中的BT64数据,还是得花接近10s才能出解。

Posted in POJ | Tagged , , | 1 Comment

基础计算几何题三道

这周一的计算概论课上,我第一次走上了讲台,奉峰哥之命,给大家讲了讲计算几何的基础。讲完后,我发现其中有几个地方没讲得太清楚,下面的解题报告中会提及部分的问题。

1、Intersecting Lines

这道题是直线的问题,用不上叉积之类的,普通的解析几何做就可以了。

先根据点的坐标求出形如Ax+By+C=0的直线方程的系数,然后根据A1/A2,B1/B2,C1/C2判断是否平行或者重合。如果既不平行也不重合,就可以直接代入手工解出来的交点公式算交点坐标。

2、Wall

最基本的凸包求解,用Graham算法实现。不过有几个实现细节有几个地方需要注意:

1)按照上课讲的选最下面的一个点(y坐标最小),如果有多个,得选x坐标最小的;

2)如果起点和两个待比较的点的连线共线,则按照坐标升序排列;

3)我当时说是按极角排序,但实际上你会发现,这是一个基于比较的排序。也就是说对于p1和p2,你只需要计算p0p1×p0p2就可以得出p1和p2相对p0的角度位置,而不用算它们的极角。我调用的是stdlib的快排,这些比较问题只需在比较函数里设置好久行了。

3、Will Indiana Jones Get There?

这道题的总体思路是,计算每条线的之间的最短距离(相交的话当然是0),然后调用一次最短路算法(我用的SPFA)。关键问题就是计算两条线段之间的最短连线距离。考虑到这道题所有的点都是水平或者竖直的,而且要算距离,向量之类的东西反而感觉实现起来不太方便,于是我采用的是分三类情况讨论:水平与水平,竖直与竖直,水平与竖直。然后根据端点坐标分别计算距离。

Posted in 学术 | Tagged , , , | 2 Comments

曾被我暴力破坏的键盘被我复活了!

没事儿又把我N久前暴力解决的键盘拆开,发现电路板没坏,只是有个发光二极管断了根针脚。随手掰了一下,结果另一个针脚也断了,而且是连根掰断…… 只得换一个。于是乎,我翻出了以前那个坏了的主板,把上面的唯一一个发光二极管用电烙铁完整地蹂躏下来,然后又把键盘电路板上的断针脚蹂躏下来,最后再把 两个重组焊了上去,然后就OK了……只是,三个灯中,有一个(当然是换上去的那个)的颜色相对有点和谐……

最终,我选择了让破Microsoft键盘顶替了超级平庸版本的Logitech。原因有以下几点:

1、Logitech超级平庸,Microsoft还有10个可爱的多媒体键;

2、和MS原配的MS鼠标等他已经等得憔悴了;

3、我亲手破坏它,现在修好了又不让他回来,太对不住了;

4、他对我的那块铜牌有功劳,Logitech想都不要想;

5、……

综上,He's back!

(有两句HP的台词,发现没?)

Posted in 杂项 | Tagged , , | Leave a comment

660+20

半夜凉初透考成绩出来了,还行,660+20(重庆的),接下,学校、专业(显然计算机)、港校面试……plenty of things to do. Yeah!

后续报道:北大信科录取

Posted in 杂项 | Tagged | Leave a comment

Odd and Even 奇数和偶数

大家都知道英语中奇数叫“odd number”,偶数叫“even number”。那你有没有疑惑过,为什么这么叫呢?
Even从古日耳曼语中来,它出现在古英语中时的意思是“水平的、不倾斜的”。偶数被2除之后,得到的两个数是相等的。如果把它们放到天平上,那么天平是水平的、没有倾斜的。所以啦,偶数就是even。
Odd从古斯堪的纳维亚语中来,经过一系列演变,有了“多余的”意思。奇数被2除余1,既然有多余了,那么奇数就是odd。
此外,odd在英文里还有“奇怪"的意思,那么,奇数从字面解释又可以看作是“奇怪的数”,简称“奇数”,是不是很巧?
有没有觉得如此推理的逻辑很奇怪呢?语言就是这么有意思!
Odd的复数odds则表示区别、差额、机会、可能性。词组“at odds”就是不和,意见不一致的意思。
Even最基本的意思就是相等的,平均的,恰好的,两不相欠的。由此产生了一个常用的词组“be even with”,意思是(偿清或报复以后)“两清了”。"Even up"也是“扯平”、“报复”的意思。
请看下面例句:
Graphs are on even pages and tables are on odd pages.
图在偶数页,表格在奇数页。
She is at odds with her boss.
她和上司不和。
The odds are that he will give his consent.
他大概会同意的。
The odds evened before the race.
赛跑前,胜负的可能性变得接近了。
I would find the killer and even it up for Tom.
我要找到凶手,替汤姆报仇。

以上内容转载自:http://paper.sznews.com/szdaily/20070606/ca2685028.htm

Posted in 杂项 | Tagged , | Leave a comment

Computational Thinking 计算(机)思维

当你女儿早晨去学校时,她把当天需要的东西放进背包;这就是预置和缓存。当你儿子弄丢他 的手套时,你建议他沿走过的路回寻;这就是回推。在什么时候你停止租用滑雪板而为自己买一对呢?这就是在线算法。在超市付账时你应当去排哪个队呢?这就是 多服务器系统的性能模型。为什么停电时你的电话仍然可用?这就是失败的无关性和设计的冗余性。完全自动的大众图灵测试是如何区分计算机和人类(简称CAPTCHA)的,即CAPTCHAs是怎样鉴别人类的?这就是充分利用求解人工智能难题之艰难来挫败计算代理程序。

这是从一篇名叫《Computational Thinking(计算思维)》摘录下来的一段比喻,个人觉得比较有意思。

原文在这里:www.cs.cmu.edu/afs/cs/usr/wing/www/publications/Wing06.pdf
原文翻译Google上满天飞

以上谨供各位大牛火星我一下~

Posted in Computer Science | Tagged , | Leave a comment

USACO Numbering the Cows(cownum)

Problem cownum: Numbering the Cows [Romanian Olympiad, 2001]

Farmer John wants to assign a (not necessarily distinct) integer

in the range 1..N (3 <= N <= 1,000) to each of his N cows for

identification purposes. Thus, he lined up all his cows in order

and came up with the following rule: the i-th cow in line must be

assigned an integer that is less than the integers assigned to cows

i+2 and i+3.

FJ wants to know the total number of possible assignments he might

make. If cow i+2 does not exist, then cow i does not have the

constraint that its ID number must be less than that of cow i+2.

Likewise if cow i+3 does not exist.

Since Farmer John is not too good at math and might be confused by

large numbers, output the answer modulo M (1 <= M <= 10,000,000).

PROBLEM NAME: cownum

INPUT FORMAT:

Two space-separated integers: N and M.

SAMPLE INPUT (file cownum.in):

3 10000

OUTPUT FORMAT:

A single line containing the number of assignments modulo M.

SAMPLE OUTPUT (file cownum.out):

9

OUTPUT DETAILS:

There are 9 valid assignments: (1,1,2), (1,1,3), (1,2,2), (1,2,3), (1,3,2),

(1,3,3), (2,1,3), (2,2,3), (2,3,3).

先orz一下xt神牛(虽然我不认识)的神奇DP

设f[i,j]表示前i头牛编号不大于j的方案数

若a[i]<>j,则总数累加f[i,j-1],否则(a[i]=j时),分以下情况累加

i)a[i-2]=a[i-1]时,总数为f[i-2,j-1]

ii)a[i-2]>a[i-1]时,总数为f[i-1,j-1]

iii)a[i-2]<a[i-1]时,总数为f[i-2,j-1]+f[i-2,j-2]+f[i-2,j-3]+...+f[i-2,1](这一处似乎和神牛的题解有些出入,他是从f[i-2,j-2]开始累加的)

边界f[1,k]=k   f[2,k]=n*n

不过现在是O(n^3),所以需要优化:g[i,j]=f[i,1]+f[i,2]+...+f[i,j]

接下来程序验证……(待续)

xt真神牛也,他的方程是对的,我的是错的。这是为什么呢?回去继续研究。

{2009年7月22日0:33:50

看过USACO题解后,终于明白哪里错了

这一句“若a[i]<>j,则总数累加f[i,j-1]”就开始错,因为a[i-1]可能为j

下面重新定义f[i,j]的计算方式:

1、用1~j-1编号这i头牛(f[i,j-1])

2、最后两头牛都为j(f[i-2,j-1])

3、最后一头为j,其他前面的小于j(f[i-1,j-1])

4、倒数第二头为j,最后一头小于j(f[i-2,j-2]+f[i-2,j-2]+f[i-2,j-3]+...+f[i-2,1])

这下就对了

2009年7月22日0:41:08}

下贴上几个月以来第一段程序代码,一年以来第一个C++代码,以及n久以来第一次切题通过记录:
[code=cpp]
/*
PROG: cownum
LANG: C++
ID: tjbwyk1
*/

#include<fstream>
using namespace std;
ifstream input("cownum.in");
ofstream output("cownum.out");

const int maxn=1002;
int f[maxn][maxn],g[maxn][maxn],n,m,i,j;

void init()
{
input>>n>>m;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for (i=1; i<=n; i++)
{
f[1][i]=i%m;
f[2][i]=i*i%m;
g[1][i]=(g[1][i-1]+f[1][i])%m;
g[2][i]=(g[2][i-1]+f[2][i])%m;
}
}

void doit()
{
for (i=3; i<=n; i++)
for (j=1; j<=n; j++)
{
f[i][j]=(f[i][j-1]+f[i-2][j-1]+f[i-1][j-1]+g[i-2][j-2])%m;
g[i][j]=(g[i][j-1]+f[i][j])%m;
}
}

void outit()
{
output<<f[n][n]<<endl;
}

int main()
{
init();
doit();
outit();
input.close();
output.close();
return 0;
}

[/code]
华丽的记录:

USER: Yikang Wang [tjbwyk1]

TASK: cownum

LANG: C++

Compiling...

Compile: OK

Executing...

Test 1: TEST OK [0.022 secs]

Test 2: TEST OK [0.022 secs]

Test 3: TEST OK [0.032 secs]

Test 4: TEST OK [0.022 secs]

Test 5: TEST OK [0.011 secs]

Test 6: TEST OK [0.022 secs]

Test 7: TEST OK [0.022 secs]

Test 8: TEST OK [0.022 secs]

Test 9: TEST OK [0.022 secs]

Test 10: TEST OK [0.054 secs]

Test 11: TEST OK [0.043 secs]

Test 12: TEST OK [0.054 secs]

Test 13: TEST OK [0.043 secs]

Test 14: TEST OK [0.054 secs]

Test 15: TEST OK [0.054 secs]

Test 16: TEST OK [0.065 secs]

All tests OK.

YOUR PROGRAM ('cownum') WORKED FIRST TIME! That's fantastic

-- and a rare thing.  Please accept these special automated

congratulations.

Test Data | Analysis

Keep up the good work!

下一个任务由烧白同学指派,Turning in Homework

Posted in USACO | Tagged | Leave a comment

开篇:重操旧业

终于又可以干老本行了,翻开USACO,还有整整一版DP等着我刷呢,就从这里开始吧。然后,第一题Numbering the Cows(cownum)就卡死了……整整三个月,我就不信……

Posted in USACO | Leave a comment