從網上看到一篇關於A+B的討論,囧……轉載一下~
【題目描述】
輸入兩個自然數,輸出他們的和
【輸入數據】
兩個自然數x和y(0<=x,y<=32767)
【輸出數據】
一個數,即x和y的和
【思路】 by cjf00000
此題考查動態規劃思想
首先我們需要把輸入字符轉換爲數字a,b 然後使用動態規劃求解A+B的值 最後輸出答案
我們可以設計出如下DP方程
令f[j]表示i+j的值 則有
- f[0][0]=0
- f[j]=max
- { f[i-1][j]+1,
- f[i][j-1]+1 }
由於對於每個i,j我們都要計算出f[j],因此時間複雜度與空間複雜度都爲O(n^2)
但是 對於題目提供的最大數字n=32767明顯超時!
【優化1】by wusu5545
對於DP方程 由於每次計算只需要f[i-1][j]或f[j-1]
因此我們可以使用滾動數組優化DP的空間複雜度
使用兩個整數x,y分別保存f[i-1][j]與f[j-1]
空間複雜度降爲O(2) 然而時間複雜度O(n^2)仍然超時!
“時間複雜度,到目前爲止還沒有更好的優化方法。因此,此題被稱爲史上最難的dp題!”
【優化2】by 匿名大牛
對於整數的運算 我們可以利用位運算的思想簡化複雜度
題目顯然要我們求兩非負整數之和。
我們知道,在非負整數加法的二進制邏輯運算中,每一位上的結果取決於以下兩方面:
1、本位上兩個邏輯位的異或值
2、後一位的結果是否溢出
利用這種性質,可以考慮如下做法:
令f[j]表示,考慮兩個加數的後i、j位相加的結果,顯然有以下狀態轉移方程
- f[j]= max
- { f[i][j-1]+y & (1 << (j-1))
- f[i-1][j]+x & (1 << (i-1)) }
賦初值f[0][0]=(x & 1) ^ (y & 1)
兩個循環變量i,j從1循環到log(2,maxint)
我們成功的把時空複雜度降爲O(log^2n)
利用滾動數組,可以進一步吧空間複雜度降爲O(2)
至此,我們得到了一個較爲圓滿的解答
【題目評價】 by Coldwings 修改 by h4x
此題實質上非常複雜
全面考察到了數學史和計算機史 經典代數 常用計算與輸入輸出 動態規劃思想以及位運算思想等
等等等知識點
考慮到題目的所有可能性 我們應當從計算機存儲的二進制的角度來逐步考慮數的表示 以字節計
數,採用多字節合用的方式表示一個大整數如今已經是高級程序語言編譯器輕鬆可以達到的目標
可是爲了加強對計算機計數的瞭解 此題可以考慮仍以最原始的方式進行計算——並且考慮最終
將二進制數轉變爲十進制輸出的全部過程 期間還考察了對ASCII碼的熟悉程度
然而此題再VIJOS上的通過人數竟然高達59% 可以看出VIJOS上大牛之多 不禁令人感嘆啊
宋神牛用最小網絡流
program problem;
var
en,et,ec,eu,ep,ex:Array[0..250000] of longint;
dis:array[0..1000] of longint;
v:array[0..1000] of boolean;
i,j,k,n,m,w,cost,l:longint;
a,b,ans,left,right:longint;
function min(a,b:longint):longint;
begin
if a<b then min:=a else min:=b
end;
procedure addedge(s,t,c,u,k:longint);
begin
inc(l);
en[l]:=en[s];
en[s]:=l;
et[l]:=t;
ec[l]:=c;
eu[l]:=u;
ep[l]:=l+k;
end;
procedure build(s,t,u,c:longint);
begin
addedge(s,t,c,u,1);
addedge(t,s,-c,0,-1);
end;
function aug(no,m:longint):longint;
var
i,d:longint;
begin
if no=n then
begin
inc(cost,m*dis[1]);
exit;
end;
v[no]:=true;
i:=ex[no];
while i<>0 do
begin
if (eu[i]>0)and not v[et[i]] and(dis[et[i]]+ec[i]=dis[no]) then
begin
d:=aug(et[i],min(m,eu[i]));
if d>0 then
begin
dec(eu[i],d);
inc(eu[ep[i]],d);
ex[no]:=i;
exit(d);
end;
end;
i:=en[i];
end;
ex[no]:=i;
exit(0);
end;
function modlabel:boolean;
var
d,i,j:longint;
begin
d:=maxlongint;
for i:=1 to n do
if v[i] then
begin
j:=en[i];
while j<>0 do
begin
if (eu[j]>0)and not v[et[j]] and(ec[j]-dis[i]+dis[et[j]]<d) then
d:=ec[j]-dis[i]+dis[et[j]];
j:=en[j]
end;
end;
if d=maxlongint then exit(true);
for i:=1 to n do
if v[i] then
begin
v[i]:=false;
inc(dis[i],d);
end;
exit(false);
end;
function work:longint;
var i:longint;
begin
cost:=0;
repeat
for i:=1 to n do ex[i]:=en[i];
while aug(1,maxlongint)>0 do
fillchar(v,sizeof(v),0);
until modlabel;
work:=cost;
end;
function solve(x,d:longint):longint;
var i,k,t,p,last,cost,lk:longint;
begin
fillchar(en,sizeof(en),0);
fillchar(dis,sizeof(dis),0);
k:=0; n:=2; t:=x; p:=0;
while x<>0 do
begin
k:=k+x mod 10;
x:=x div 10;
inc(p);
end;
n:=1; x:=t; l:=k+p+1; last:=1; cost:=1; lk:=0;
while x<>0 do
begin
k:=x mod 10;
for i:=1 to k do
begin
inc(n);
build(last,n,1,-cost);
build(n,last+k+1,1,0);
end;
cost:=cost*10;
inc(n);
if last<>1 then
begin
if lk<k then
build(1,last,k-lk,0);
if k<lk then
build(last,n,lk-k,0);
end;
last:=n; x:=x div 10;
if lk<k then lk:=k;
end;
build(1,n,1,d);
solve:=-work;
end;
begin
readln(a,b);
left:=1; right:=1000000000;
while right-left>15000 do
begin
ans:=(left+right)shr 1;
if solve(ans,b)>a then
right:=ans
else left:=ans;
end;
for i:=left to right do
if solve(i,b)=a then
begin
writeln(i);
halt;
end;
end.
還有一位用什麼一個PASCAL的面向對象的解法
program p1000;
var a,b,c:qword;
function max(a,b:qword):qword;
begin
if a<b
then exit(b)
else exit(a);
end;
function max(a,b:qword):qword;
begin
if a>b
then exit(b)
else exit(a);
end;
operator :=(a:qword):b:qword;
begin
b:=0;
if max(a,b)=a
then b:=max(a,b)
else b:=min(a,b);
end;
operator +(a,b:qword)c:qword;
begin
c:=0;
c:=max(a,b)+min(a,b);
end;
begin
readln(a,b);
c:=a+b;
writeln(c);
end.