申請SAE

如果您發現本博客的外觀很難看,那是因為部分外觀文件被中國.國家.防火.牆屏.蔽所致!
請翻~牆!

我的Wordpress博客的地址: http://zhuyf.tk/
顯示具有 高精度 標籤的文章。 顯示所有文章
顯示具有 高精度 標籤的文章。 顯示所有文章

2011年11月8日 星期二

[高精度計算]OI練習題 :回文數 huiwen 解題報告

【問題描述】
若一個數(首位不爲0)從左到右讀與從右到左讀都是一樣,這個數就叫做迴文數,例如12512就是一個迴文數。
給定一個N進制正整數,把它的各位數字上數字倒過來排列組成一個新數,然後與原數相加,如果是迴文數則停止,如果不是,則重複這個操作,直到和爲迴文數爲止。例如:10進制87則有:
STEP1: 87+78=165
STEP2: 165+561=726
STEP3: 726+627=1353
STEP4: 1353+3531=4884
任務:寫一個程序,給定一個N(2≤N≤10,N=16)進制數m(10~15用小寫字母a~f表示),m的位數上限爲20。求最少經過幾步可以得到迴文數。如果在30步以內(包括30步)不可能得到迴文數,則輸出“impossible”,否則輸出該迴文數及生成該迴文數的最少步數。
【輸入格式】
文件有兩行,每行一個數,即N和N進制整數m
【輸出格式】
如果輸入文件給定的數據在30步以內(包括30步)不可能得到迴文數,則輸出文件只有一行,即輸出“impossible”。
否則輸出文件爲兩行。第一行是由輸入文件給定數據生成的迴文數,第二行是生成該迴文數的最少步數。
【輸入輸出樣例】
輸入
10
87
輸出
4884
4
【分析】
這題屬於基本練習類題目,需要用到高精度計算。
每次把數組翻過來,進行比對,如果相同則輸出step,否則這個數加上它的“相反數”。

【我的代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int N;
int T=0;

class hugeint
{
public:
    int len;
    int num[500];
    hugeint()
    {
        len=0;
        memset(num,0,sizeof(num));
    }
};

hugeint ans;
hugeint tmp;

hugeint add()
{
    int i;
    hugeint Ans;
    hugeint a=ans;
    hugeint b=tmp;
   
    if (a.len>b.len)
        Ans.len=a.len;
    else
        Ans.len=b.len;
    for (i=1;i<=Ans.len;i++)
    {
        Ans.num[i]+=(a.num[i]+b.num[i]);
        Ans.num[i+1]+=Ans.num[i]/N;
        Ans.num[i]%=N;
    }
    if (Ans.num[Ans.len+1]>0)
        Ans.len++;
    return Ans;
}

bool check()
{
    memset(tmp.num,0,sizeof(tmp.num));
    tmp.len=ans.len;
    int top=tmp.len;
    for (int i=1;i<=ans.len;i++)
    {
        tmp.num[top]=ans.num[i];
        top--;
    }
   
    for(int i=1;i<=ans.len;i++)
    {
        if(tmp.num[i]!=ans.num[i])
            return false;
        top--;
    }
    return true;
}

void init()
{
    scanf("%d\n",&N);
    string str;
    cin>>str;
   
    ans.len=str.length();
    for (int i=1;i<=ans.len;i++)
    {
        if(str[ans.len-i]>='0' && str[ans.len-i]<='9')
            ans.num[i]=str[ans.len-i]-'0';
        else
            ans.num[i]=str[ans.len-i]-'a'+10;
    }
    return;
}

void works()
{
    while(T<=30)
    {
        if(check())
        {
            for (int i=ans.len;i>=1;i--)
            {
                if(ans.num[i]<=9)
                    cout<<ans.num[i];
                else
                    cout<<char(ans.num[i]-10+'a');
            }
            printf("\n%d\n",T);
            return;
        }
        ans=add();
        T++;
    }
    cout<<"impossible"<<endl;
}

int main()
{
    freopen("huiwen.in","r",stdin);
    freopen("huiwen.out","w",stdout);
    init();
    works();
    return 0;
}

2011年11月6日 星期日

[高精度運算]OI練習題:火星上的加法運算 madition 解題報告

【題目描述】
最近歡歡看到一本有關火星的書籍,其中她被一個加法運算所困惑,由於她的運算水平有限,她想向你求助,作爲一位優秀的程序員,你當然不會拒絕。
輸入文件(madition.in):
第一行先輸入一個運算的進制N(2<=N<=36),接下來的兩行爲需要進行運算的字符,其中每個字符串的長度不超過200位.其爲N進制的數。其中包括0-9及a-z(代表10-35).
輸出文件(madition.out):
在N進制下它們的和.
輸入樣例1:
20
1234567890
abcdefghij
輸出樣例1:
bdfi02467j
輸入樣例2:
20
99999jjjjj
9999900001
輸出樣例2:
iiiij00000


【分析】
高精度運算的變形。只需要注意 高精度計算時 滿N進一,以及最後輸出後把大於10的數轉化為小寫字母。

【我的代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

class hugeint
{
public:
    int len;
    int num[300];
    hugeint()
    {
        len=1;
        memset(num,0,sizeof(num));
    }
}Ans,A,B;
int N;

void init()
{
    scanf("%d\n",&N);
    char str1[300],str2[300];
    memset(str1,'\0',sizeof(str1));
    memset(str2,'\0',sizeof(str2));
    scanf("%s\n%s",&str1,&str2);
   
    int la=strlen(str1);
    int lb=strlen(str2);
   
    A.len=la;
    B.len=lb;
    int tmp;
    for (int i=0;i<la;i++)
    {
        if(str1[i]>='a' && str1[i]<='z')
            tmp=str1[i]-'a'+10;
        else
            tmp=str1[i]-'0';
        A.num[la-i]=tmp;
    }
   
    for (int i=0;i<lb;i++)
    {
        if(str2[i]>='a' && str2[i]<='z')
            tmp=str2[i]-'a'+10;
        else
            tmp=str2[i]-'0';
        B.num[lb-i]=tmp;
    }   
   
    return;
}

void add()
{
    int i;
    if (A.len>B.len)
        Ans.len=A.len;
    else
        Ans.len=B.len;
    for (i=1;i<=Ans.len;i++)
    {
        Ans.num[i]+=(A.num[i]+B.num[i]);
        Ans.num[i+1]+=Ans.num[i]/N;
        Ans.num[i]%=N;
    }
    if (Ans.num[Ans.len+1]>0)
        Ans.len++;
    return;
}

void print()
{
    for (int i=Ans.len;i>=1;i--)
    {
        if(Ans.num[i]<10)
            cout<<Ans.num[i];
        else
            cout<<(char)(Ans.num[i]-10+'a');
    }
    return;
}

int main()
{
    freopen("madition.in","r",stdin);
    freopen("madition.out","w",stdout);
    init();
    add();
    print();
    return 0;
}

2011年10月25日 星期二

NOIP2007普及組 hanoi雙塔問題

【問題描述】
給定A、B、C三根足夠長的細柱,在A柱上放有2n箇中間有孔的圓盤,共有n個不同的尺
寸,每個尺寸都有兩個相同的圓盤,注意這兩個圓盤足不加區分的(下圖爲n=3的情形)。現要將
這些圓盤移到C柱上,在移動過程中可放在B柱上暫存。要求:
(1)每次只能移動一個圓盤;
(2)A、B、C三根細柱上的圓盤都要保持上小下大的順序;
任務:設An爲2n個圓盤完成上述任務所需的最少移動次數,對於輸入的n,輸出An。

【輸入】
輸入文件hanoi.in爲一個正整數n,表示在A柱上放有2n個圓盤。

【輸出】
輸出文件hanoi.out僅一行,包含一個正整數,爲完成上述任務所需的最少移動次數An。

【輸入輸出樣例1】
hanoi.in
1
hanoi.out
2

【輸入輸出樣例2】
hanoi.in
2
hanoi.out
6

【限制】
對於50%的數據,1<=n<=25
對於100%的數據,l<=n<=200

【提示】
設法建立An與An-1的遞推關係式。

【分析】
設F(x)表示移動2x個圓盤的最小步數。
很容易推出F(x)=2*X^2-2
或者這樣寫:F(x)=F(x-1)*2 最後的F(n)再減2即可,這樣寫可以把高精度乘法換成高精度加法。

【滿分代碼】
//hanoi雙塔問題
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;

char result[1000]; 
void Highj(char a[],char b[]) 
{ 
    int x[1000],y[1000]; 
    int i,l,la,lb; 
    char temp[1000]; 
    for (i=0;i<999;i++){x[i]=0;y[i]=0;} //初始化 
    la=strlen(a); 
    lb=strlen(b); 
     
    //把char型數組 按正倒序  變成整型數組 
    if (la<=lb) 
    { 
        for (i=la-1;i>=0;i--) 
            y[i]=a[la-1-i]-'0'; 
        for (i=lb-1;i>=0;i--) 
            x[i]=b[lb-1-i]-'0'; 
        l=la; 
    } 
    else  
    { 
        for (i=la-1;i>=0;i--) 
            x[i]=a[la-1-i]-'0'; 
        for (i=lb-1;i>=0;i--) 
            y[i]=b[lb-1-i]-'0';     
        l=lb;         
    } 
     
    //執行加法運算 
    for (i=0;i<l;i++) 
    { 
        x[i]=x[i]+y[i]; 
        if (x[i]>=10)  
        { 
            x[i]-=10; 
            x[i+1]+=1; 
        } 
    } 
     
    //再把整型數組按照倒序轉換成char型數組 
    for (i=999;i>=0;i--) 
        temp[999-i]=x[i]+'0'; 
     
    //從不是0的一位開始輸出結果 
    for (i=0;i<=999;i++) 
    { 
        if (temp[i]>'0') 
        { 
            for(int j=i;j<=999;j++) 
                result[j-i]=temp[j]; 
            return; 
        } 
    } 
    //如果沒有一位大於0 
    result[0]='0'; 
    return; 
}  

int main()
{
 freopen("hanoi.in","r",stdin);
 freopen("hanoi.out","w",stdout);
 long long num=1;
 int n;
 cin>>n;
 if (n<90)
 {
  for (int i=1;i<=n;i++)
   num=num*2;
  num=num*2-2;
  cout<<num<<endl;
  return 0;
 }
 if (n>=90)
 {
  char two[1];
  two[0]='2';
  memset(result,'\0',sizeof(result));
  result[0]='1';
  for (int i=1;i<=n+1;i++) 
  {
   Highj(result,result);
  }
  int len=strlen(result);
  if (result[len-1]-'0'>=2)
   result[len-1]=result[len-1]-2;
  else 
  {
   result[len-1]=result[len-1]-2+10;
   result[len-1]=result[len-2]-2;
  }
  cout<<result<<endl;
 }
 return 0;
}


2011年10月23日 星期日

NOIP2007複賽提高組 矩陣取數遊戲 解題報告

3、矩陣取數遊戲 (game.pas/c/cpp)

【問題描述】
帥帥經常更同學玩一個矩陣取數遊戲:對於一個給定的n*m的矩陣,矩陣中的每個元素aij據爲非負整數。遊戲規則如下:
1. 每次取數時須從每行各取走一個元素,共n個。m次後取完矩陣所有的元素;
2. 每次取走的各個元素只能是該元素所在行的行首或行尾;
3. 每次取數都有一個得分值,爲每行取數的得分之和;每行取數的得分 = 被取走的元素值*2i,其中i表示第i次取數(從1開始編號);
4. 遊戲結束總得分爲m次取數得分之和。
帥帥想請你幫忙寫一個程序,對於任意矩陣,可以求出取數後的最大得分。
【輸入】
輸入文件game.in包括n+1行;
第一行爲兩個用空格隔開的整數n和m。
第2~n+1行爲n*m矩陣,其中每行有m個用單個空格隔開
【輸出】
輸出文件game.out僅包含1行,爲一個整數,即輸入矩陣取數後的最大的分。
【輸入輸出樣例1】


game.in
game.out
2 3
1 2 4
3 4 2
82
【輸入輸出樣例1解釋】
第1次:第一行取行首元素,第二行取行尾元素,本次的分為1*21+2*21=6
第2次:兩行均取行首元素,本次得分爲2*22+3*22=20
第3次:得分爲3*23+4*23=56。總得分爲6+20+56=82
【輸入輸出樣例2】

game.in
game.out
1 4
4 5 0 5
122

【輸入輸出樣例3】
game.in
game.out
2 10
96 56 54 46 86 12 23 88 80 43
16 95 18 29 30 53 88 83 64 67
316994

【限制】
60%的數據滿足:1<=n, m<=30,答案不超過1016
100%的數據滿足:1<=n, m<=80,0<=aij<=1000




動態規劃。由於每行是互不影響的,可以把每行孤立開看,對每行分別進行一次動態規劃計算。結果可能很大,需要高精度計算。爲避免計算高精度乘法,可以在初始化時把每個數的2^k遞推算出。
狀態設定
F[i,j] 爲當前行取前i個數和後j個數的最大得分。
V[i,j] 爲當前行第i個數乘以2^j的值。
狀態轉移方程
F[i,j]=max{ F[i-1,j] + V[i,p] , F[i,j-1] + V[M-j+1,p] } p=i+j(表示當前是第幾次取數)
V[i,j]=V[i,j-1]+V[i,j-1]
邊界條件
V[i,1]=當前行第i個數的值
F[0,0]=0
每行結果
Max{ F[i,M-i] } (0<=i<=M)
最終結果
每行結果的總和


【代碼】
由於數據很強,在我的電腦上的評測結果是:超時2組。
#include <iostream>
#include <string>
#include <cstring>
using namespace std;

const int SIZE=50;
class hugeint
{
public:
    unsigned int len;
    unsigned int num[SIZE];
    hugeint()
    {
        len=0;
        memset(num,0,sizeof(num));
    }
};

hugeint V[82][82];//V[i,j]為當前行第i個數乘以2^j的積
hugeint A[82];//A為讀入的矩陣的每一行
hugeint F[82][82];//F[i,j]為當前行取前i個數和後j個數的最大得分
hugeint tsum;
hugeint Two;
int N,M;//行數,列數

hugeint times(hugeint a,hugeint b)
{
    unsigned  int i,j;
    hugeint ans;
    memset(ans.num,0,sizeof(ans.num));
    for (i=1;i<=a.len;i++)
        for (j=1;j<=b.len;j++)
            ans.num[i+j-1]+=a.num[i]*b.num[j];
    for (i=1;i<=a.len+b.len;i++)
    {
        ans.num[i+1]+=ans.num[i]/10;
        ans.num[i]=ans.num[i]%10;
    }
    if (ans.num[a.len+b.len]>0)
        ans.len=a.len+b.len;
    else
        ans.len=a.len+b.len-1;
    return ans;
}

hugeint add(hugeint a,hugeint b)
{
    unsigned int i;
    hugeint ans;
    memset(ans.num,0,sizeof(ans.num));
    if (a.len>b.len)
        ans.len=a.len;
    else
        ans.len=b.len;
    for (i=1;i<=ans.len;i++)
    {
        ans.num[i]+=(a.num[i]+b.num[i]);
        ans.num[i+1]+=ans.num[i]/10;
        ans.num[i]%=10;
    }
    if (ans.num[ans.len+1]>0)
        ans.len++;
    return ans;
}

hugeint average(hugeint a,hugeint b)
{
    int i;
    hugeint ans;
    ans=add(a,b);
    for (i=ans.len;i>=2;i--)
    {
        ans.num[i-1]+=(ans.num[i]%2)*10;
        ans.num[i]/=2;
    }
    ans.num[i]/=2;
    if (ans.num[ans.len]==0)
        ans.len--;
    return ans;
}

bool over(hugeint a,hugeint b)
{
    int i;
    if (a.len<b.len)
        return false;
    if (a.len>b.len)
        return true;
    for (i=a.len;i>=1;i--)
    {
        if (a.num[i]<b.num[i])
            return false;
        if (a.num[i]>b.num[i])
            return true;
    }
    return false;
}

void PreDP()
{
    for (int i=1;i<=M;i++)
        V[i][0]=A[i];
    for (int i=1;i<=M;i++)
    {
        for (int j=1;j<=M;j++)
            V[i][j]=times(Two,V[i][j-1]);
    }
}

hugeint DP()
{
    hugeint sum;
    int p;
    for (int i=0;i<=M;i++)
    {
        for (int j=0;j<=M;j++)
        {
            hugeint m;
            p=i+j;
            if(p>M)
                continue;
            if (i-1>=0)  
            {
                if ( over( add(F[i-1][j],V[i][p]),m     )  )
                    m=add(F[i-1][j],V[i][p]);
            }
            if (j-1>=0)
            {
                if ( over( add(F[i][j-1],V[M-j+1][p]),m )  )
                    m=add(F[i][j-1],V[M-j+1][p]);
            }
            F[i][j]=m;
        }
    }
    for (int i=0;i<=M;i++)
    {
        if(  over(F[i][M-i],sum)  )
            sum=F[i][M-i];
    }
    return sum;
}

void init()
{
    Two.len=1;
    Two.num[1]=2;
    cin>>N>>M;
    char word[10]={'\0'};
    hugeint target;
    for (int i=1;i<=N;i++)
    {
        for (int j=1;j<=M;j++)
        {
            memset(word,'\0',sizeof(word));
            cin>>word;
            memset(target.num,0,sizeof(target.num));
            target.len=strlen(word);
            for (unsigned int k=1;k<=target.len;k++)
                target.num[k]=word[target.len-k]-'0';
            A[j]=target;
        }
        PreDP();
        tsum=add(tsum,DP());
    }
}

int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    init();
    for (int i=tsum.len;i>=1;i--)
        cout<<tsum.num[i];
    return 0;
}

2011年10月17日 星期一

NOIP2011提高组初赛 C++组 完善程序第一题 大整数开方

NOIP2011提高组初赛 C++组  完善程序第一题  大整数开方

输入一个正整数n(1<= n <10^100),试用二分法计算它的平方根的整数部分。

这是我补全的代码,已经运行通过:

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

const int SIZE=200;

struct hugeint
{
    int len,num[SIZE];
};

hugeint times(hugeint a,hugeint b)
{
    int i,j;
    hugeint ans;
    memset(ans.num,0,sizeof(ans.num));
    for (i=1;i<=a.len;i++)
        for (j=1;j<=b.len;j++)
            ans.num[i+j-1]+=a.num[i]*b.num[j];
    for (i=1;i<=a.len+b.len;i++)
    {
        ans.num[i+1]+=ans.num[i]/10;
        ans.num[i]=ans.num[i]%10;
    }
    if (ans.num[a.len+b.len]>0)
        ans.len=a.len+b.len;
    else
        ans.len=a.len+b.len-1;
    return ans;
}

hugeint add(hugeint a,hugeint b)
{
    int i;
    hugeint ans;
    memset(ans.num,0,sizeof(ans.num));
    if (a.len>b.len)
        ans.len=a.len;
    else
        ans.len=b.len;
    for (i=1;i<=ans.len;i++)
    {
        ans.num[i]+=(a.num[i]+b.num[i]);
        ans.num[i+1]+=ans.num[i]/10;
        ans.num[i]%=10;
    }
    if (ans.num[ans.len+1]>0)
        ans.len++;
    return ans;
}

hugeint average(hugeint a,hugeint b)
{
    int i;
    hugeint ans;
    ans=add(a,b);
    for (i=ans.len;i>=2;i--)
    {
        ans.num[i-1]+=(ans.num[i]%2)*10;
        ans.num[i]/=2;
    }
    ans.num[i]/=2;
    if (ans.num[ans.len]==0)
        ans.len--;
    return ans;
}

hugeint plustwo(hugeint a)
{
    int i;
    hugeint ans;
   
    ans=a;
    ans.num[1]+=2;
    i=1;
    while ((i<=ans.len) && (ans.num[i]>=10) )
    {
        ans.num[i+1]+=ans.num[i]/10;
        ans.num[i]%=10;
        i++;
    }
    if (ans.num[ans.len+1]>0)
        ans.len++;
    return ans;
}

bool over(hugeint a,hugeint b)
{
    int i;
    if (a.len<b.len)
        return false;
    if (a.len>b.len)
        return true;
    for (i=a.len;i>=1;i--)
    {
        if (a.num[i]<b.num[i])
            return false;
        if (a.num[i]>b.num[i])
            return true;
    }
    return false;
}

int main()
{
    freopen("highsquare.in","r",stdin);
    freopen("highsquare.out","w",stdout);
    string s;
    int i;
    hugeint target,left,middle,right;
   
    cin>>s;
    memset(target.num,0,sizeof(target.num));
    target.len=s.length();
    for (i=1;i<=target.len;i++)
        target.num[i]=s[target.len-i]-'0';
    memset(left.num,0,sizeof(left.num));
    left.len=1;
    left.num[1]=1;
    right=target;
    do
    {
        middle=average(left,right);
        hugeint sp=times(middle,middle);
        if ( over(sp,target) )
            right=middle;
        else
            left=middle;
    }while (!over(plustwo(left),right));
   
    for (i=left.len;i>=1;i--)
        cout<<left.num[i];
    cout<<endl;
    return 0;
}