UVa694 The Collatz Sequence

Step1:
任选一个正整数A作为这个数列的第一项。
Step2:
如果A=1则停止。
Step3:
如果A为偶数,则A=A/2然后重新回到Step2。
Step4:
如果A为奇数,则A=3*A+1然后重新回到Step2。
这个演算法已经被证明当首项小于等于109时这个数列最终都会在Step2停止,但是有些A值在这个数列中会超出许多电脑的整数上限。在这个问题中我们想​​要计算这个数列的长度,而数列的终止有两种情况:1.最终会在Step2停止或是2.某一项会在Step4超出一个特定的上限。

Input

输入包含许多组待测资料,每一列代表一组待测资料,每组待测资料包含两个正整数,第一个数为首项A,第二个数为这个数列的上限L,无论A或L都不会大于2,147,483,647(32位元有号整数的最大值),且首项A总是小于上限L。当输入为两个负数时代表输入结束。

Output

 

对每组待测资料必须输出它为第几组(从1开始),一个冒号,首项A的值,上限L的值,以及此一数列的项数。

#include<stdio.h>
int main()
{long n,m,i,A,t=1;
while(scanf("%ld%ld",&n,&m)!=EOF){
    A=n;
    if(n<0&&m<0)    break;
    if(n==1)        break;
    for(i=0;;){
        if(n%2==0)  {n/=2;i++;}
        else        {n=(3*n+1);i++;}
        if(n>m)     break;
        if(n==1)    {i++;break;}
    }
    printf("Case %ld: A = %ld, limit = %ld, number of terms = %ld\n",t,A,m,i);
    t++;
}
return 0;
}

 

UVa 100 The 3n + 1 problem

考慮以下的演算法:

1.         輸入 n
2.         印出 n
3.         如果 n = 1 結束
4.         如果 n 是奇數 那麼 n=3*n+1
5.         否則 n=n/2
6.         GOTO 2

例如輸入 22, 得到的數列: 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

據推測此演算法對任何整數而言會終止 (當列印出 1 的時候)。雖然此演算法很簡單,但以上的推測是否真實卻無法知道。然而對所有的n ( 0 < n < 1,000,000 )來說,以上的推測已經被驗證是正確的。

給一個輸入 n ,透過以上的演算法我們可以得到一個數列(1作為結尾)。此數列的長度稱為n的cycle-length。上面提到的例子, 22 的 cycle length為 16.

問題來了:對任2個整數i,j我們想要知道介於i,j(包含i,j)之間的數所產生的數列中最大的 cycle length 是多少。

Input

輸入可能包含了好幾列測試資料,每一列有一對整數資料 i,j 。

0< i,j < 1,000,000

Output

對每一對輸入 i , j 你應該要輸出  i, j 和介於 i, j 之間的數所產生的數列中最大的 cycle length。

#include <stdio.h>

int cycle_length(int n)
{
    int length = 1;
    while (n!=1) {
        if (n % 2 == 1){
            n = 3 * n + 1;
        } else {
            n /= 2;
        }
        length++;
    }
    return length;
}

int main()
{
    int n, i, j ,k , max, oi, oj;
    while (scanf("%d %d",&i, &j) == 2){
        oi = i; oj = j;
        max = 0;
        if (i > j){
            k = i;
            i = j;
            j = k;
        }

        for (n = i; n <= j; n++){
            k = cycle_length(n);
            if (k > max) max = k;
        }
        printf("%d %d %d\n", oi, oj, max);
    }
}

 

[演算法笔记]字串變整數

#include <stdio.h>
#include <iostream>
using namespace std;

int string_length(char *s)
{
    int n = 0;
    while (s[n]) n++;
    return n;
}

int pow10(int exp)
{
    int n = 1;
    for (int i = 0; i < exp; i++)
        n *= 10;
    return n;
}

void string_to_integer()
{
    char s[10] = "26962869";
    int length =string_length(s);
    
    int n = 0;
    for (int i = 0; i < length; i++)
        n += (s[i] - '0') * pow10(length - 1 - i);

    cout << n;
}

void string_to_integer2()
{
    char s[10] = "26962869";

    int n = 0;
    for (int i = 0; s[i]; i++)
        n = n * 10 + s[i] - '0';

    cout << n;
}

 

UVA846 Steps

一個人沿著一數線前進。他每次走的長度(整數)必須是正的,而且比上一步走的長度多1,一樣,或少1。另外請注意:第一步及最後一步的長度一定是1.

請問這個人若要從x走到y,最少需要走幾步。

舉例說明:若x=45, y=50,那麼每步走的長度可以是:1,2,1,1,所以最少需要4步。

Input

輸入的第一列有一個正整數代表以下有幾組測試資料。每組測試資料一列,含有2個整數x,y(0 <= x <= y < 231)。

Output

每組測試資料輸出一列,最少需要走幾步才能從x走到y。

Sample Input

3
45 48
45 49
45 50

Sample Output

3
3
4

找出步數與距離的關係即可得解。

  • 0步最多能抵達的距離是0
  • 1步最多能抵達的距離是1(1)
  • 2步最多能抵達的距離是2(1 1)
  • 3步最多能抵達的距離是4(1 2 1)
  • 4步最多能抵達的距離是6(1 2 2 1)
  • 5步最多能抵達的距離是9(1 2 3 2 1)
  • 6步最多能抵達的距離是12(1 2 3 3 2 1)
  • ……以此類推

 

题意:在给出的两个数A,B,找出最少需要几步可以从A到B,规则:第一步和最后一步必须是1,还有中途只可以比前一数大一,等于,或小一。。

第一种方法:从A,B开始依次加,然后距离减去步长,直到 dis<0 ,我们可以肯定的是,小于零的这部分C,一定小于当前的步长N,所以我们一定能在之前的步长中找到(N-C),也就可以填满这一部分。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
 
int n;
 
int main()
{
    int t;
    scanf("%d",&t);
    while (t--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        int dis = b - a ;
        bool flag = false ;
        int count = 0 ;
        int step = 1 ;
        while (dis > 0 )
        {
            dis -= step;
            count++;
            if (flag)
                step++;
            flag = !flag;
        }
        printf("%d\n",count);
    }
    return 0;

第二种方法:1+2+…..+(n-1)+n+(n-1)+….2+1 =n^2,这是我们在达到和为n^2在满足题意的情况下能确定的最少步数,设想我们如果 dis==n^2 ,那结果就显而易见了,当dis > n^2的时候,首先我们先确定最大的n满足 n^2 <= dis ,然后我们只要再找dis-n^2的数就可以,如果dis-n^2<=n,那只要再找一个,如果>n,但我们发现这个数一定<(n+1)^2-n^2

,那这个数一定可以化为数列中的两个数。。。

/*
Author:ZCC;
Solve:
// 所走步数对应的序列如果左右对称,那么所走的步数应该是最少的。因为最先一步和最后一步长度均必须是
// 1,又由于每一步的长度必须是非负整数,且要么比上一步步长恰好大 1,要么相等,要么小 1,则左右对
// 称的序列能在最少步数得到最大距离。如果采取左右对称的走法,设两点距离为 distance,n = sqrt
// (distance),则由于最大步数为 n 步时能达到的距离是 1 + 2 + 3 + ... + (n - 1) + n +
// (n - 1) + ... + 3 + 2 + 1 = n^2。比较两点距离 distance 与 n^2,若相等,表明只需走
// 2 * (n - 1) + 1 = 2 * n - 1 步,否则若剩余距离 distance - n^2 在 1 - (n + 1),
// 只需插入一步即可,若 distance - n^2 大于 (n + 1),则需多插入两步即可。
*/

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
 
int main()
{
    int t ;
    scanf("%d",&t);
    while (t--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        int dis = b - a ;
        if (dis <= 3)
            printf("%d\n",dis);
        else
        {
            
            int n = sqrt(dis);
            if ( n * n == dis)
                printf("%d\n",2*n-1);
            else if ((dis-n*n)<=n)
                printf("%d\n",2*n);
            else  printf("%d\n",2*n+1);
        }
    }
    return 0;
}

列出几项,找到规律:

1: 1
2 1 1
3 1 1 1
4: 1 2 1
5 1 2 1 1
6 1 2 2 1
7 1 2 2 1 1
8 1 2 2 2 1
9: 1 2 3 2 1
10 1 2 3 2 1 1
11 1 2 3 2 2 1
12 1 2 3 3 2 1
13 1 2 3 3 2 1 1
14 1 2 3 3 2 2 1
15 1 2 3 3 3 2 1
16: 1 2 3 4 3 2 1

可以得出 n * n : 2 * n – 1

综合得出:

n * n: 1……n……1 minstep: 2 * n – 1.

n * ( n + 1 ) 1…..n,n…….1 minstep: 2 * n

(n + 1) * ( n + 1 ) :1……n+1………1 minstep: 2 * n + 1

代码:

#include <stdio.h>
#include <math.h>
 
int main()
{
    int ncase;
    scanf("%d", &ncase);
    while (ncase--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        x = y - x;
        if (x == 0)//debug
            printf("0\n");
        else
        {
            y = (int)sqrt(x);
            if (x == y * y)
            {
                printf("%d\n", 2 * y - 1);
            }
            else if (x <= y * (y + 1))
            {
                printf("%d\n", 2 * y);
            }
            else
            {
                printf("%d\n", 2 * y + 1);
            }
        }
    }
    return 0;
}

 

UVa 10167 Birthday Cake

题目:给你2N个点。输出一条直线Ax+By=0,使得在直线两边均有n个点,点不在直线上。

分析:搜索、枚举。因为区间是A,B区间是[-500,500],直接枚举即可;

判断在直线的方向,直接带入方程求解。

说明:暴力(⊙_⊙)!

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
 
using namespace std;
 
typedef struct pnode
{
    int x,y;
}point;
point P[100];
 
int main()
{
    int n;
    while ( ~scanf("%d",&n) && n ) {
        for ( int i = 0 ; i < 2*n ; ++ i )
            scanf("%d%d",&P[i].x,&P[i].y);
        
        int flag = 1;
        for ( int A = -500 ; flag && A <= 500 ; ++ A )
        for ( int B = -500 ; flag && B <= 500 ; ++ B ) {
            int l = 0,r = 0;
            for ( int i = 0 ; i < 2*n ; ++ i ) {
                l += (A*P[i].x+B*P[i].y>0);
                r += (A*P[i].x+B*P[i].y<0);
            }
            if ( l == n && r == n ) {
                printf("%d %d\n",A,B);
                flag = 0;
            }
        }
    }
    return 0;
}

 

UVa 10929 You can say 11

你的任務是,給你一個正整數 N,判定它是否是 11 的倍數。

Input

每列資料有一個正整數N,N 最大可能到 1000 位數。

若 N = 0 代表輸入結束。

Output

對每個輸入的數,輸出是否為 11 的倍數。輸出格式請參考 Sample Output。

 

方法1:直接从个位数开始mod 11计算。

 /*0.026s*/  
  
#include<cstdio>  
#include<cstring>  
  
char s[1005];  
  
int main(void)  
{  
    int remainder;  
    while (gets(s), strcmp(s, "0"))  
    {  
        remainder = 0;  
        for (int i = 0; s[i]; i++)  
            remainder = (remainder * 10 + (s[i] & 15)) % 11;  
        if (remainder) printf("%s is not a multiple of 11.\n", s);  
        else printf("%s is a multiple of 11.\n", s);  
    }  
    return 0;  
}

备注:&15按位与的意思 每位同时为1才为1,否则为0

 

方法2:若一个整数的奇位数字之和与偶位数字之和的差能被11整除,则这个数能被11整除。

证明:比如num = 10000a4+1000a3+100a2+10a1+a0=(a4+a2+a0-a3-a1)+(9999a4+99a2+1001a3+11a1)

因为11 | 99,所以11 | (9900+99)

因为11 | 1111,所以11 | (1111-110)

所以…

 /*0.025s*/  
  
#include<cstdio>  
#include<cstring>  
  
char s[1005];  
  
int main(void)  
{  
    int l, i, sum;  
    while (gets(s), strcmp(s, "0"))  
    {  
        l = strlen(s), sum = 0;  
        for (i = 0; i < l; i += 2) sum += s[i] & 15;  
        for (i = 1; i < l; i += 2) sum -= s[i] & 15;  
        if (sum % 11) printf("%s is not a multiple of 11.\n", s);  
        else printf("%s is a multiple of 11.\n", s);  
    }  
    return 0;  
}

 

UVA 10878 Decode the tape

【解析】:

输入从上往下看,可以看成题目所说的一段磁带。
题目给的信息很少,因此大部分信息要从输入输出得到。
(相当于给你一段明文跟密码,然后你破解其中的加密规则)

首先我们发现,磁带一共有43行,跟密码的字符个数一样(换行包括在内)。
可以猜测是否磁带的一行,代表一个字符。
然后我们可以发现,密码中相同的字符,在磁带里面的对应行,也是相同的。
更加坚定我们的猜测。

然后我们观察磁带里每行的结构。
其整体的格式一样,只有 “o” 的位置和数量有不同。
而且 “o” 只会在固定的7个位置出现。
则 7 个位置,一共可以表示出 2^7=128 种字符。
联系字符的整型特征(ASCII码),
可以猜测,磁带的每行表示着一个二进制数,这个二进制数的数值正好是对应字符的ASCII码。

看一下空格(ASCII码为32)的对应行 “ o . ”,
把行中的空格看作0,“o” 看作1,则可以得到二进制数0100000,正好是32。
破译完毕。

/*********************************
*   日期:2013-5-3
*   作者:SJF0115
*   题号: 10878 - Decode the tape
*   来源:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=20&page=show_problem&problem=1819
*   结果:AC
*   来源:UVA
*   总结:
**********************************/
#include <stdio.h>
#include <string.h>
 
int c[] = { 0, 0, 64, 32, 16, 8, 0, 4, 2, 1, 0};
 
int main() {
    char str[15];
    int value,i;
    //freopen("C:\\Users\\XIAOSI\\Desktop\\acm.txt","r",stdin);   
    gets(str);
    while(gets(str) && str[0] != '_'){
        value = 0;
        int len = strlen(str);
        for(i = 2;i < len;i++){
            if(str[i] == 'o'){
                value += c[i];
            }
        }
        printf("%c",value);
    }
}

 

[演算法笔记]小画家到墨水

void flood(int x, int y, int new_color, int old_color)
{
    if (x>=0 && x<10 && y>=0 && y<10)
        if (image[x][y] == old_color)
        {
            image[x][y] = new_color;
 
            // 寫成一個迴圈
            for (int i=0; i<4; i++)
            {
                static int dx[4] = {1, -1, 0, 0};
                static int dy[4] = {0, 0, 1, -1};
                flood(x + dx[i], y + dy[i], new_color, old_color);
            }
        }
}

 

[演算法笔记]尋找總和為 10 的區間

void find_interval(int array[], int n, int num)
{
    int sum = 0;
    for (int i=0, j=0; j<=n; )  // 枚舉區間[i, j)
    {
        if (sum > num)
            sum -= array[i++];
        else
            sum += array[j++];
 
        if (sum == num)
            cout << '[' << i << ',' << j-1 << ']';
    }
}

 

[演算法笔记]英文單字從單數變複數

void plural(string s)
{
    int n = s.length();
    if (s.back() == 'y')
        cout << s.substr(0, n-1) << "ies";
    else if (s.back() == 's' || s.back() == 'x')
        cout << s << "es";
    else if (s.substr(n-2) == "sh" || s.substr(n-2) == "ch")
        cout << s << "es";
    else if (s.substr(n-3) == "man")
        cout << s.substr(0, n-3) << "men";
    else
        cout << s << 's';
}