算法竞赛入门经典(第2版)—第四章(函数和递归)

零碎知识点整理
  • main()函数的返回0代表正常结束,返回给其他调用的程序(如自动评测系统),避免评测系统认为程序异常退出了。
  • hypot(x,y)函数计算x和y的平方和,其中x和y都是double类型,返回值也是double类型。
  • 当a是变量时,&a表示取遍历a的地址。当a是地址时,*a表示a指向是变量。
  • \r是回车,\n是换行,\t是缩进,相当于一个tab键。
    • Unix系统里,每行结尾只有“<换行>”,即“\n”;
      Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;
      Mac系统里,每行结尾是“<回车>”,即“\r”。
      一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix下打开的话,在每行的结尾会多车一个^M字符。
  • double类型比较大小
    在这里插入图片描述
    参考博文:ACM中浮点数精度处理
  • C语言函数传二维数组
Node G[MAX][MAX]
bool solve(int stj, int stk, int len, Node (*G)[MAX])
{
}
solve(j, k, i, G)
题目
1339 - Ancient Cipher

题目链接:1339 - Ancient Cipher

  • 题目大意:给定两个字符串,判断两个字符串是否存在一个字符映射表,使得一个字符可以转换为另一个字符串。
  • 思路:水题。主要的思维的转化。可以将题目转换为比较两个字符串中各个字符出现次数是否存在一对一相等的情况。那么就可以先统计两个字符串中不同字符出现的次数,然后排序再比较各个次数数据是否相等即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;

const int MAX = 200;
char s1[MAX], s2[MAX];
map<char, int>m1, m2;//将不同字符映射成数字,作为数组的下标
int cnt1, cnt2, a1[MAX], a2[MAX];//存储次数数据

int main()
{
    while(scanf("%s%s", s1, s2)!=EOF)
    {
        cnt1 = 0, cnt2 = 0;
        memset(a1, 0, sizeof(a1));
        memset(a2, 0, sizeof(a2));
        m1.clear(), m2.clear();
        int len1 = strlen(s1), len2 = strlen(s2);
        //统计两个字符串各个字符出现的次数
        for(int i=0; i<len1; i++)
        {
            if(m1.count(s1[i])==0)//第一次遇到该字符
            {
                m1[s1[i]] = cnt1;
                a1[cnt1++] = 1;
            }else
            {
                a1[m1[s1[i]]]++;
            }
        }
        for(int i=0; i<len2; i++)
        {
            if(m2.count(s2[i])==0)
            {
                m2[s2[i]] = cnt2;
                a2[cnt2++] = 1;
            }else
            {
                a2[m2[s2[i]]]++;
            }
        }
        if(cnt1!=cnt2)//两个字符串长度不相等
        {
            printf("NO\n");
            continue;
        }
        //排序
        sort(a1, a1+cnt1);
        sort(a2, a2+cnt2);
        int flag = 1;
        //比较字符出现次数是否相等
        for(int i=0; i<cnt1; i++)
        {
            if(a1[i]!=a2[i])
            {
                flag = 0;
                break;
            }
        }
        if(flag) printf("YES\n");
        else     printf("NO\n");
    }
    return 0;
}
489 - Hangman Judge

题目链接:489 - Hangman Judge

  • 题目大意:刽子手游戏其实是一款猜单词游戏,如图4-1所示。游戏规则是这样的:计算机想一个单词让你猜,你每次可以猜一个字母。如果单词里有那个字母,所有该字母会显示出来;如果没有那个字母,则计算机会在一幅“刽子手”画上填一笔。这幅画一共需要7笔就能完成,因此你最多只能错6次。注意,猜一个已经猜过的字母也算错。
    在本题中,你的任务是编写一个“裁判”程序,输入单词和玩家的猜测,判断玩家赢了(You win.)、输了(You lose.)还是放弃了(You chickened out.)。每组数据包含3行,第1行是游戏编号(-1为输入结束标记),第2行是计算机想的单词,第3行是玩家的猜测。后两行保证只含小写字母。
  • 思路:模拟水题。不要想太多,要读懂题意。直接模拟即可。

代码:

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

string s1, s2;
int T, flag[10000];
int main()
{
    while(cin >> T && T!=-1)
    {
        cin >> s1 >> s2;
        memset(flag, -1, sizeof(flag));
        int len1 = s1.size(), len2 = s2.size(), cnt = 0, wro = 0, ans = 2;
        //遍历求解
        for(int i=0; i<len2; i++)
        {
            int k = 0;
            for(int j=0; j<len1; j++)
            {
                //字符相等,且该字符可遍历
                if(s1[j]==s2[i] && flag[j]==-1)
                {
                    flag[j] = 0;
                    cnt++;//记录答对的字符个数
                    k = 1;
                }
            }
            if(!k) wro++;//一个相等的也没有,则答错了一个字符
            if(wro==7)//失败
            {
                ans = 1;
                break;
            }
            if(cnt==len1)//成功答对所有字符
            {
                ans = 0;
                break;
            }
        }
        cout << "Round " << T << endl;
        if(ans==0)
            cout << "You win." << endl;
        else if(ans==1)
            cout << "You lose." << endl;
        else
            cout << "You chickened out." << endl;
    }
    return 0;
}
133 - The Dole Queue

题目链接:133 - The Dole Queue

  • 题目大意:输入n、k和m,n为人数,逆时针序号为1~n,A从1按逆时针数到第k个人停下来,B从n开始按顺时针数到第m个人停下来,输出被数到的人,并且让这些人出局。然后从当前位置继续以上操作。输出出局人的序号(输出格式参见原题说明)。
  • 思路:双向约瑟夫环问题,用模拟法来解决。具体细节见代码。

代码:

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

const int MAX = 50;
int flag[MAX];
struct Node
{
    int a, b;
    Node(int a1=-1, int b1=-1): a(a1), b(b1){}
};
int N, k, m, Mod;

int main()
{
    while(scanf("%d%d%d", &N, &k, &m)!=EOF &&(N+k+m))
    {
        memset(flag, 0, sizeof(flag));
        Node ans[MAX];//巨鹿出局编号
        Mod = N;
        int cntA = 0, cntB = N-1, num = 0, nA, nB, cnt = 0;
        while(num<N)
        {
            nA = 0, nB = 0;
            //逆时针(按序)找到第k个
            while(1)
            {
                if(flag[cntA]==0) nA++;
                if(nA==k) break;
                cntA = (cntA+1)%Mod;
            }
            //顺时针(逆序)找到第m个
            while(1)
            {
                if(flag[cntB]==0) nB++;
                if(nB==m) break;
                cntB = (cntB-1+Mod)%Mod;
            }
            //出局一人
            if(flag[cntA]==0)
            {
                flag[cntA] = 1;
                num++;//出局人数
                ans[cnt].a = cntA+1;//记录该人编号
            }
            //出局一人
            if(flag[cntB]==0)
            {
                flag[cntB] = 1;
                num++;
                ans[cnt].b = cntB+1;
            }
            cnt++;
        }
        //按格式打印输出
        for(int i=0; i<cnt; i++)
        {
            if(i) printf(",");
            if(ans[i].a!=-1)
                printf("%3d", ans[i].a);
            if(ans[i].b!=-1)
                printf("%3d", ans[i].b);
        }
        printf("\n");
    }
    return 0;
}
213 - Message Decoding

题目链接:

  • 题目大意:将字符串使用01串序列编码,输入编码后的字符串,输出原字符串。
  • 思路:模拟题。找出字符串编码的规律,目标给出一个01串找到对应的字符,则需要根据01串的长度len和二进制值转十进制的值d得到目标字符的下标,即i=d+ ∑ j = 1 l e n − 1 2 j \sum_{j=1}^{len-1}2^j j=1len12j。然后就是根据输入分割出每一个01编码串,带入以上式子求出下标,得到对应字符,然后累加储存输出就可。

代码:

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

string s, a, ans;
char ch;
int Pow(int t)
{
    int n = 1;
    for(int i=0;i<t; i++)
        n *= 2;
    return n;
}
//找到对应的字符
char trans(int id, int len)
{
    int num = 0;
    for(int i=1; i<len; i++)
    {
        num += Pow(i)-1;
    }
    num += id;
    return s[num];
}
int main()
{
    while(getline(cin, s))
    {
        a = "", ans = "";
        int st = 0, ed = 0, len = 0;
        while(1)
        {
            len = 0, st = ed;
            ed += 3;
            //输入长度
            for(int i=st; i<ed; i++)
            {
                ch = getchar();
                if(ch=='\n' || ch=='\r')
                {
                    i--;
                    continue;
                }
                len = len*2+ch-'0';
            }
            if(len==0) break;//如果长度为0,退出
            while(1)
            {
                st = ed;
                ed += len;
                int id = 0, cnt = 0;
                //输入字符
                for(int i=st; i<ed; i++)
                {
                    ch = getchar();
                    if(ch=='\n' || ch=='\r')
                    {
                        i--;
                        continue;
                    }
                    id = id*2+ch-'0';
                    if(ch=='1') cnt++;
                }
                if(cnt==len) break;//如果字符全'1',退出
                ans += trans(id, len);//累加存储结果
            }
        }
        getchar();
        printf("%s\n", ans.c_str());
    }
    return 0;
}
512 - Spreadsheet Tracking

题目链接:512 - Spreadsheet Tracking

代码:
参考博文:UVA - 512 Spreadsheet Tracking

1589 - Xiangqi

题目链接:1589 - Xiangqi

  • 题目大意:模拟题。象棋判断是否将死。给出红帅的位置和N个黑棋的位置,判断是否能够将红帅将死,黑棋有将、车、马、炮。下一步该黑棋走。
  • 思路:判断考虑的比较多。首先如果将帅碰面,红棋自接胜出,黑棋失败。然后就需要遍历红帅能够遍历的各个点,然后判断,黑棋能不能将住这几个点。同时如果红帅吃掉黑棋一个子(位置相同),那么遍历的时候就不需要再遍历该棋子了。车需要考虑的是车和帅行或列相同时,它们之间没有其它子。将需要考虑的是只有当它与帅之间没有其它子(肯定是列相同)。炮需要考虑的是炮与帅行或列相同是,它们之间只有一个子。马需要考虑的是它遍历到帅的位置时,没有其它子在马腿的位置。当帅每个遍历,均有黑棋看着时,方可黑棋胜,否则看做没有将死。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

const int MAX = 15;
struct Node
{
    char s;
    int x, y;
}T[MAX];
int G[MAX][MAX];
int dx[5] = {0, 1, -1, 0};
int dy[5] = {1, 0, 0, -1};//右下上左
//用于马的遍历
int dx2[10] = {2, 1, -2, -1, 2, 1, -2, -1};
int dy2[10] = {1, 2, 1, 2, -1, -2, -1, -2};
int dx3[10] = {1, 0, -1, 0, 1, 0, -1, 0};
int dy3[10] = {0, 1, 0, 1, 0, -1, 0, -1};
int N, x, y;

bool check_G(int j, int r, int c)
{
    //直接碰面,则将死
    int ty = T[j].y;
    for(int k=r+1; k<T[j].x; k++)
    {
        if(G[k][ty]==1)//中间有棋子
            return 0;
    }
    return 1;
}
bool check_R(int j, int r, int c)
{
    //中间没有棋子,则将死
    //列相同和行相同
    if(c==T[j].y)
    {
        int tx = T[j].x, ty = T[j].y;
        int st = min(tx, r)+1, ed = max(tx, r);
        for(int k=st; k<ed; k++)//在其间判断是否有棋子
        {
            if(G[k][ty]==1)
                return 0;
        }
        return 1;
    }
    else if(r==T[j].x)
    {
        int tx = T[j].x, ty = T[j].y;
        int st = min(ty, c)+1, ed = max(ty, c);
        for(int k=st; k<ed; k++)
        {
            if(G[tx][k]==1)
                return 0;
        }
        return 1;
    }
    return 0;
}
bool check_C(int j, int r, int c)
{
    //中间有一个棋子,可以将死
    int cnt = 0;//记录中间棋子数
    if(c==T[j].y)
    {
        int tx = T[j].x, ty = T[j].y;
        int st = min(tx, r)+1, ed = max(tx, r);
        for(int k=st; k<ed; k++)
        {
            if(G[k][ty]==1)
                cnt++;
        }
    }
    else if(r==T[j].x)
    {
        int tx = T[j].x, ty = T[j].y;
        int st = min(ty, c)+1, ed = max(ty, c);
        for(int k=st; k<ed; k++)
        {
            if(G[tx][k]==1)
                cnt++;
        }
    }
    if(cnt==1) return 1;
    else       return 0;
}
bool check_H(int j, int r, int c)
{
    //遍历可以到达的地点,如果没有绊着马腿,达到帅,则将死
    for(int k=0; k<8; k++)
    {
        int chx = T[j].x+dx2[k], chy = T[j].y+dy2[k];//达到的坐标
        if(chx==r && chy==c)
        {
            int cx = T[j].x+dx3[k], cy = T[j].y+dy3[k];//马腿
            if(G[cx][cy]==0) return 1;
            else             return 0;
        }
    }
    return 0;
}
int main()
{
    while(cin >> N >> x >> y && N)
    {
        memset(G, 0, sizeof(G));
        int flag = 1, pre = -1;
        for(int i=0; i<N; i++)
        {
            cin >> T[i].s >> T[i].x >> T[i].y;
            G[T[i].x][T[i].y] = 1;
            if(T[i].s=='G' && T[i].y==y)//有可能直接将和帅碰面,则失败
            {
                pre = i;
            }
        }
        if(pre!=-1)
        {
            for(int j=x+1; j<T[pre].x; j++)
            {
                if(G[j][y]==1)//判断是否将帅碰面
                {
                    flag = 0;
                    break;
                }
            }
        }
        if(flag && pre!=-1)
        {
            printf("NO\n");
            continue;
        }
        flag = 1;
        int s = -1;
        for(int i=0; i<4; i++)
        {
            int f = 0;
            int r = x+dx[i], c = y+dy[i];
            if(r<1 || r>3 || c<4 || c>6) continue;
            for(int j=0; j<N; j++)
            {
                if(T[j].x==r && T[j].y==c) continue;
                s = j;
                if(T[j].s=='G' && (abs(r-x)!=1 || c-y!=0) && c==T[j].y)
                {
                    if(check_G(j, r, c))
                    {
                        f = 1;
                        break;
                    }
                }
                else if(T[j].s=='R')
                {
                    if(check_R(j, r, c))
                    {
                        f = 1;
                        break;
                    }
                }
                else if(T[j].s=='C')
                {
                    if(check_C(j, r, c))
                    {
                        f = 1;
                        break;
                    }
                }
                else if(T[j].s=='H')
                {
                    if(check_H(j, r, c))
                    {
                        f = 1;
                        break;
                    }
                }
            }
            if(!f)//不能阻止将移动
            {
                flag = 0;
                break;
            }
        }
        if(flag) printf("YES\n");
        else     printf("NO\n");
    }
    return 0;
}
201 - Squares

题目链接:201 - Squares

  • 题目大意:n个点组成的正方体,其中有m条边,H i j表示边(i,j)~(i,j+1),V j i表示边(i,j)~(i+1,j),问不同边长的子正方体各有多少个。
  • 思路:模拟题。我的思路是先按输入标记边,设计结构体Node(h, v),当H i j时,G[i][j].h = 1,当V i j时,G[i][j].v = 1。然后遍历当边长为1到n-1时正方形的个数,使用ans[]存储。判断正方形的方法就是判断四条长为len的边是否存在。

代码:

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

const int MAX = 15;
struct Node
{
    int h, v;
    Node(int h=0, int v=0): h(h), v(v){}
};
int ans[MAX], m, n, a, b;
char ch;

//判断起始点为(stj,stk)且边长为len的正方形是否存在
bool solve(int stj, int stk, int len, Node (*G)[MAX])
{
    for(int i=stj; i<stj+len; i++)
    {
        if(G[i][stk].v!=1) return 0;
        if(G[i][stk+len].v!=1) return 0;
    }
    for(int i=stk; i<stk+len; i++)
    {
        if(G[stj][i].h!=1) return 0;
        if(G[stj+len][i].h!=1) return 0;
    }
    return 1;
}
int main()
{
    int T = 1;
    while(scanf("%d", &n)!=EOF)
    {
        Node G[MAX][MAX];
        scanf("%d", &m);
        memset(ans, 0, sizeof(ans));
        for(int i=0; i<m; i++)
        {
            getchar();//要使用getchar(),去回车
            scanf("%c", &ch);
            scanf("%d%d", &a, &b);
            if(ch=='H')
                G[a][b].h = 1;
            else
                G[b][a].v = 1;
        }
		//遍历正方形
        for(int i=1; i<n; i++)
        {
            for(int j=1; j<=n-i; j++)
            {
                for(int k=1; k<=n-i; k++)
                {
                    if(solve(j, k, i, G))
                    {
                        ans[i]++;
                    }
                }
            }
        }
        if(T!=1) printf("\n**********************************\n\n");
        printf("Problem #%d\n\n", T++);
        int flag = 1;
        for(int i=1; i<n; i++)
        {
            if(ans[i])
            {
                printf("%d square (s) of size %d\n", ans[i], i);
                flag = 0;
            }
        }
        if(flag) printf("No completed squares can be found.\n");
    }
    return 0;
}
220 - Othello

题目链接:220 - Othello

  • 题目大意:题意参考博文uva 220 - Othello(黑白翻转棋)
  • 思路:大模拟题。这种题思路不会太难,就是复杂,考虑的细节比较多。具体细节见代码。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
using namespace std;

char G[15][15];
//右、下、左、上、右下、左下、右上、左上
int dx[10] = {0, 1, 0, -1, 1, 1, -1, -1};
int dy[10] = {1, 0, -1, 0, 1, -1, 1, -1};
struct Node
{
    int x, y;
    Node(int x=-1, int y=-1): x(x), y(y){}
};
vector<Node> pos;
map<char, char>m;
char player, order[5];

//方便切换选手
void Init()
{
    m['W'] = 'B', m['B'] = 'W';
}
//搜索下一步可以放置的位置
void Find_next(char player)
{
    pos.clear();
    for(int i=1; i<=8; i++)
    {
        for(int j=1; j<=8; j++)//遍历棋盘
        {
            if(G[i][j]!='-') continue;
            for(int k=0; k<8; k++)//遍历方向
            {
                int x = i+dx[k], y = j+dy[k];
                if(x<1 || x>8 || y<1 || y>8) continue;
                int x1 = x, y1 = y;
                if(G[x1][y1]==m[player])//周围有其它颜色的棋子
                {
                    int flag = 0;
                    while(1)//按照原方向继续遍历
                    {
                        x1 = x1+dx[k], y1 = y1+dy[k];
                        if(x1<1 || x1>8 || y1<1 || y1>8) break;
                        if(G[x1][y1]==player)//符合条件退出
                        {
                            flag = 1;
                            break;
                        }
                        else if(G[x1][y1]=='-') break;//不符合条件退出
                    }
                    if(flag)
                    {
                        pos.push_back(Node(i, j));//记录符合条件的位置
                        break;
                    }
                }
            }
        }
    }
}
//判断该位置对于当前选手是否可以下棋子(大致的思路同上一个函数)
bool check(char player, int a, int b)
{
    for(int k=0; k<8; k++)
    {
        int x = a+dx[k], y = b+dy[k];
        if(x<1 || x>8 || y<1 || y>8) continue;
        int x1 = x, y1 = y;
        if(G[x1][y1]==m[player])
        {
            int flag = 0;
            while(1)
            {
                x1 = x1+dx[k], y1 = y1+dy[k];
                if(x1<1 || x1>8 || y1<1 || y1>8) break;
                if(G[x1][y1]==player)
                {
                    flag = 1;
                    break;
                }
                else if(G[x1][y1]=='-') break;
            }
            if(flag)//满足条件
            {
                return 1;
            }
        }
    }
    return 0;
}
//移动并修改棋盘(大致的思路同上一个函数)
void Modify(char player, int a, int b)
{
    for(int k=0; k<8; k++)
    {
        int x = a+dx[k], y = b+dy[k];
        if(x<1 || x>8 || y<1 || y>8) continue;
        int x1 = x, y1 = y;
        if(G[x1][y1]==m[player])
        {
            int flag = 0;
            while(1)
            {
                x1 = x1+dx[k], y1 = y1+dy[k];
                if(x1<1 || x1>8 || y1<1 || y1>8) break;
                if(G[x1][y1]==player)
                {
                    flag = 1;
                    break;
                }
                else if(G[x1][y1]=='-') break;
            }
            if(flag)//找到可以改变棋子颜色的方向线
            {
                G[a][b] = player;
                G[x][y] = player;
                x1 = x, y1 = y;
                while(1)
                {
                    x1 = x1+dx[k], y1 = y1+dy[k];
                    if(G[x1][y1]!=m[player]) break;
                    else G[x1][y1] = player;
                }
            }
        }
    }
}
//数当前棋盘中黑色和白色棋子的数目
void Count()
{
    int w = 0, b = 0;
    for(int i=1; i<=8; i++)
    {
        for(int j=1; j<=8; j++)
        {
            if(G[i][j]=='W') w++;
            else if(G[i][j]=='B') b++;
        }
    }
    printf("Black - %2d White - %2d\n", b, w);
}
//打印当前棋盘
void PrintG()
{
    for(int i=1; i<=8; i++)
    {
        for(int j=1; j<=8; j++)
            printf("%c", G[i][j]);
        printf("\n");
    }
}
int main()
{
    int T;
    Init();
    scanf("%d", &T);
    int t = T;
    while(T--)
    {
        for(int i=1; i<=8; i++)
        {
            scanf("%s", G[i]+1);
        }
        getchar();//吃掉回车
        scanf("%c", &player);
        if(t!=T+1) printf("\n");
        while(scanf("%s", order))
        {
            if(order[0]=='Q')
            {
                PrintG();
                break;
            }
            else if(order[0]=='L')
            {
                Find_next(player);
                if(pos.size())
                {
                    for(int i=0; i<pos.size(); i++)
                    {
                        if(i) printf(" ");
                        printf("(%d,%d)", pos[i].x, pos[i].y);
                    }
                    printf("\n");
                }
                else printf("No legal move.\n");
            }
            else if(order[0]=='M')
            {
                int a = order[1]-'0', b = order[2]-'0';
                if(check(player, a, b))//判断该选手能否下
                {
                    Modify(player, a, b);
                    Count();
                }
                else
                {
                    player = m[player];//切换选手
                    Modify(player, a, b);
                    Count();
                }
                player = m[player];//该下一个选手
            }
        }
    }
    return 0;
}
253 - Cube painting

题目链接:253 - Cube painting

  • 题目大意:给一个字符串(按序表示正方体的6个面),前6个字符表示一个正方体的6个面,判断后6个字符是否与前6个字符表示同一个正方体。
  • 思路:简单题。同一个正方体无论如何旋转,对面是不会改变的。因此求出每一个字符串表示的整体的对面对应关系,比较一下就可知。

代码:

#include<iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;

int cnt1, cnt2;
pair<char, char>p1[10], p2[10];
char s[20];

//按序存储对面字符
void solve(pair<char, char> (*p), char s[], int st)
{
    p[0].first = s[st], p[0].second = s[st+5];
    p[1].first = s[st+5], p[1].second = s[st];
    p[2].first = s[st+2], p[2].second = s[st+3];
    p[3].first = s[st+3], p[3].second = s[st+2];
    p[4].first = s[st+1], p[4].second = s[st+4];
    p[5].first = s[st+4], p[5].second = s[st+1];
}

int main()
{
    while(scanf("%s", s)!=EOF)
    {
        cnt1 = 0, cnt2 = 0;
        solve(p1, s, 0);
        solve(p2, s, 6);
        //排序
        sort(p1, p1+6);
        sort(p2, p2+6);
        int flag = 1;
        //同一正方体的对面应该相同
        for(int i=0; i<6; i++)
        {
            if(p1[i].first!=p2[i].first || p1[i].second!=p2[i].second)
            {
                flag = 0;
                break;
            }
        }
        if(flag) printf("TRUE\n");
        else     printf("FALSE\n");
    }
    return 0;
}
1590 IP Networks

题目链接:1590 IP Networks

  • 题目大意:给出一个IP地址集合,求出其对应的最小子网的网络号和掩码。
  • 思路:大模拟题。思想很简单,主要是细节,可能集合中所有IP地址均相同。首先需要经输入的IP地址转换为二进制表示,然后比较得到其最早不想同的位置,记录其ansj和ansk(分别表示第ansj段,该段中的第ansk位)。然后根据ansj和ansk即可输出对应的网络号和掩码。
    • 对于网络号:可知在ansj段之前均相同,为输入的IP地址的前ansj-1段,对于ansj段,需要通过二进制转十进制,即将ansk之后的均为0,得到十进制后输出。同时还需要输出ansj之后的段,均为0,还有’.'相隔。
    • 对于掩码:可知在ansj段之前均是’255.’,在ansj段中,在ansk位之后均为0,然后将二进制转为十进制,输出。同时还需要输出ansj之后的段,均为0,还有’.'相隔。
    • IP地址转二进制:按段转二进制,每一段对应一个数组。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;

const int MAX = 1001;
char IP[MAX][5][10], s[50];
int m, a[5];
stack<int> stc;
//IP地址转二进制
void trans_IP(char s[], int f)
{
    sscanf(s, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
    for(int j=0; j<4; j++)
    {
        int b = a[j];
        while(b)
        {
            stc.push(b%2);
            b /= 2;
        }
        while(stc.size()<8) stc.push(0);//注意可能长度不足8
        int cnt = 0;
        while(!stc.empty())
        {
            //cout << c
            int c = stc.top(); stc.pop();
            IP[f][j][cnt++] = c+'0';
        }
    }
}
//输出对应的IP地址
void Print_IP(int ansj, int ansk)
{
    int cnt = 0, k = 0, j = ansj;
    //前ansj-1段
    while(cnt<j)
    {
        if(s[k]=='.')
        {
            cnt++;
        }
        printf("%c", s[k++]);
    }
    //第ansj段
    int ans = 0;
    for(int i=0; i<8; i++)
    {
        if(i<ansk)
            ans = ans*2+IP[0][j][i]-'0';
        else
            ans *= 2;
    }
    printf("%d", ans);
    while(j<3)//一定要判断
    {
        printf(".");
        printf("0");
        j++;
    }
    printf("\n");
}
//输出对应的掩码
void Print_mask(int ansj, int ansk)
{
    //cout << ansk << endl;
    int j = ansj;
    for(int i=0; i<j; i++)
    {
        printf("255.");
    }
    int ans = 0;
    for(int i=0; i<8; i++)
    {
        if(i<ansk)
            ans = ans*2+1;
        else
            ans *= 2;
    }
    printf("%d", ans);
    while(j<3)
    {
        printf(".0");
        j++;
    }
    printf("\n");
}
int main()
{
    while(scanf("%d", &m)!=EOF)
    {
        int ansj=-1, ansk=-1;
        for(int i=0; i<m; i++)
        {
            scanf("%s", s);
            trans_IP(s, i);
        }
        int flag = 1;
        //寻找最早不相同的位置
        for(int j=0; j<4; j++)
        {
            for(int k=0; k<8; k++)
            {
                for(int i=0; i<m-1; i++)
                {
                    if(IP[i][j][k]!=IP[i+1][j][k])
                    {
                        flag = 0;
                        ansj = j, ansk = k;
                        break;
                    }
                }
                if(!flag) break;
            }
            if(!flag) break;
        }
        //有可能集合中所有的P地址均相等
        if(ansj==-1)//注意
        {
            printf("%s\n", s);
            printf("255.255.255.255\n");
            continue;
        }
        Print_IP(ansj, ansk);
        Print_mask(ansj, ansk);
    }
    return 0;
}
12108 - Extraordinarily Tired Students

题目链接:12108 - Extraordinarily Tired Students

  • 题目大意:每个学生(1<=n<=10)存在一个awake-sleep周期,当这个学生到awake的最后一刻时,他要判断当前睡觉和醒的学生的人数,如果睡觉的人数绝对大于醒着的人数,那么他要继续保持清醒a分钟,否则就进入睡觉状态。
  • 思路:比较水的模拟题。主要难点在状态更新,思路逻辑一定要清晰。如果存在循环则不存在同时苏醒的情况。

代码:

#include <iostream>
#include <cstdio>
using namespace std;

const int MAX = 15;
struct Node
{
    int a, b, c, p, st;//p是总周期,st是初始状态c,用来判断是否存在循环
};
Node T[MAX];
int n;
int main()
{
    int Case = 1;
    while(scanf("%d", &n)==1 && n)
    {
        int nump = 0, numc = 0, floop = 0, ans;//nump之前轮睡觉人数,numc当前轮睡觉人数
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d", &T[i].a, &T[i].b, &T[i].c);
            T[i].p = T[i].a+T[i].b, T[i].st = T[i].c;
            if(T[i].c>T[i].a) nump++, numc++;
        }
        printf("Case %d: ", Case++);
        if(numc==0)
        {
            printf("1\n");
            continue;
        }
        for(int t=2; ;t++)//从第2轮开始,因为第一轮已经判断过了
        {
            nump = numc, floop = 0;
            for(int i=0; i<n; i++)
            {
                if(T[i].c==T[i].p) numc--;//判断该苏醒了
                if(T[i].c==T[i].a && nump>n/2)//判断可以睡了
                    numc++;
                else if(T[i].c==T[i].a)//判断不可以睡
                    T[i].c = 0;
                T[i].c = (T[i].c+1)%T[i].p;//获得当前轮的状态
                if(T[i].c==0) T[i].c = T[i].p;//修正状态为1-p
                if(T[i].c==T[i].st) floop++;//累加
            }
            if(floop==n) break;//循环
            if(numc==0)//全部苏醒
            {
                ans = t;
                break;
            }
        }
        if(floop==n)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}
815 - Flooded!

题目链接:815 - Flooded!

  • 题目大意 :在一个m*n的网格中,每一个格子有其海拔高度,向网格中注水,水面高度一致,给定网格中总水量,求出水面高度已经被水覆盖的地区的百分比。
  • 思路:我采用的是二分搜索的方法。先判断以最高的海拔高度为水面,是否能够装下所有水量,如果不能够,则继续添加,之后的添加相当于在长方体中添加水。如果能够则,二分求水面高度,判断该水面高度是否可以满足所有水量。重点是判断的double类型在比较是有精度限制,本题的精度限制为1e-5,在比较大小时控制精度有利于减少运行时间,在判断==时,则必须使用精度判断,如a-b<1e-5 && a-b>-1e-5

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int MAX = 1000;
int n, m, k;
double a[MAX], flood;
double ans, per, num;

//判断水面是否符合条件
double solve(double mid)
{
    num = 0;
    double sum = 0;
    for(int i=0; i<k; i++)
    {
        if(mid<a[i]) break;
        sum += (mid-a[i])*100;
        num++;
    }
    return sum;
}
int main()
{
    int T = 1;
    while(scanf("%d%d", &m, &n)!=EOF && m)
    {
        k = 0;
        for(int i=0; i<m; i++)
        {
            for(int j=0; j<n; j++)
                scanf("%lf", &a[k++]);
        }
        scanf("%lf", &flood);
        sort(a, a+k);
        double lb = a[0], ub = a[k-1], sum = 0, mid, cnt;
        for(int i=0; i<k; i++)
            sum += (ub-a[i])*100;
        if(sum>flood)
        {
            for(int i=0; i<100; i++)
            {
                mid = (lb+ub)/2;
                if(solve(mid)<=flood) lb = mid;
                else                  ub = mid;
            }
            ans = lb;
            per = num/(n*m*1.0)*100;
        }
        else
        {
            cnt = (flood-sum)/(m*n*100);
            ans = ub+cnt;
            num = m*n;
            //减去大于等于水平面的区域
            for(int i=k-1; i>=0; i--)
            {
                if(ans>a[i]) break;
                num--;
            }
            per = num/(n*m*1.0)*100;
        }
        printf("Region %d\n", T++);
        printf("Water level is %.2f meters.\n", ans);
        printf("%.2f percent of the region is under water.\n\n", per);
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值