挑战程序设计竞赛(第二章习题总结)

搜索
Curling 2.0(POJ 3009)

题目链接:Curling 2.0
参考博文:POJ 3009 Curling 2.0(DFS + 模拟)

  • 题目大意:题意比较复杂,详见参考博文。
  • 思路:重要的是理解清楚题意。障碍物可以撞碎,球在遇见障碍物之前一定是直行。DFS回溯方法比较适合。具体思路参考博文。

代码:

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

typedef long long LL;

const int MAX = 25;
int maze[MAX][MAX];

int dx[5] = {0, 1, 0, -1};
int dy[5] = {1, 0, -1, 0};
int ans;
int w, h;
int sx, sy;

//判断坐标的状态
int check(int x, int y)
{
    if(maze[x][y]==0 || maze[x][y]==2) return 1;//可以移动
    else if(maze[x][y]==-1 || maze[x][y]==1) return 2;//不能移动
    else return 3;//到达目的地
}

void dfs(int x, int y, int t)
{
    if(maze[x][y]==3 || t>10)//步数超出规定以及到达目的地均结束
    {
        if(ans>t) ans = t;
    }
    else
    {
        //四个方向遍历
        for(int i=0; i<4; i++)
        {
            int tx = x, ty = y;
            if(check(tx+dx[i], ty+dy[i]) != 2)//只进行可以移动的状态操作
            {
                while(check(tx+dx[i], ty+dy[i])==1)//如果前方可以移动,一直向同一个方向移动,步数不更新
                    tx += dx[i], ty += dy[i];

                if(maze[tx+dx[i]][ty+dy[i]]==1)//前方障碍物
                {
                    //当前方是障碍物时,撞碎变成0,更新了地图
                    maze[tx+dx[i]][ty+dy[i]] = 0;
                    t++;
                    dfs(tx, ty, t);//继续从障碍物前一个开始
                    t--;
                    maze[tx+dx[i]][ty+dy[i]] = 1;
                }
                else if(maze[tx+dx[i]][ty+dy[i]]==3)//到达目的地
                {
                    t++;
                    dfs(tx+dx[i], ty+dy[i], t);//直接到达目的地,结束
                    t--;
                }
            }
        }
    }
}

int main()
{
    while(scanf("%d%d", &w, &h)!=EOF && (w+h))
    {
        memset(maze, -1, sizeof(maze));
        ans = 1<<29;
        for(int i=1; i<=h; i++)
        {
            for(int j=1; j<=w; j++)
            {
                scanf("%d", &maze[i][j]);
                if(maze[i][j]==2)
                    sx = i, sy = j;
            }
        }

        dfs(sx, sy, 0);
        if(ans>10)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}
Meteor Shower(POJ 3669)

题目链接:Meteor Shower

  • 题目大意:给m个炸弹,炸弹在第t秒钟会在(x,y)处爆炸,爆炸会波及周围的四个点。求离(0,0)最近的没有被爆炸波及的点。
  • 思路:广搜。将安全区标记为-1,将爆炸区标记为该点最早爆炸的时间点,bfs遍历爆炸区,目标到达任意一个安全区。注意一个点可能爆炸多次,需要去最早爆炸的时间。
    代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int MAX = 500;

struct Node
{
    int x, y, t;
    Node(int x=-1, int y=-1, int t=-1): x(x), y(y), t(t){}
};
queue<Node> q;
int M, X, Y, T;
int vis[MAX][MAX];//-1代表安全区,0代表已经遍历的地区,其余正值代表爆炸的时间
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};

void bfs(Node s)
{
    while(!q.empty()) q.pop();
    q.push(s);
    if(vis[s.x][s.y]<0)
    {
        printf("%d\n", &s.t);
        return;
    }
    if(vis[s.x][s.y]>0 && s.t<vis[s.x][s.y])
        vis[s.x][s.y] = 0;

    while(!q.empty())
    {
        Node u = q.front(); q.pop();
        for(int i=0; i<4; i++)
        {
            int x = u.x+dx[i], y = u.y+dy[i], t = u.t+1;
            if(x>=0 && y>=0)
            {
                if(vis[x][y]>0 && t<vis[x][y])
                {
                    vis[x][y] = 0;
                    q.push(Node(x, y, t));
                }
                else if(vis[x][y]<0)
                {
                    printf("%d\n", t);
                    return;
                }
            }
        }
    }
    printf("-1\n");
}

int main()
{
    scanf("%d", &M);
    memset(vis, -1, sizeof(vis));
    for(int i=0; i<M; i++)
    {
        scanf("%d%d%d", &X, &Y, &T);

        //同一个点可能会爆炸多次,取最早爆炸的一次
        if(vis[X][Y]==-1)
            vis[X][Y] = T;
        else if(vis[X][Y]>T)
            vis[X][Y] = T;
        for(int j=0; j<4; j++)
        {
            int x = X+dx[j], y = Y+dy[j];
            if(x>=0 && y>=0)
            {
                if(vis[x][y]==-1)
                    vis[x][y] = T;
                else if(vis[x][y]>T)
                {
                    vis[x][y] = T;
                }
            }
        }
    }
    bfs(Node(0, 0, 0));
    return 0;
}
Smallest Difference(POJ 2718)

题目链接:Smallest Difference

  • 题目大意:将k个数字分成两份,组成两个整数,求其最小的差值。
  • 思路:全排列的求解搜索。因为最多是0-9,所以如果全排列可知有428800中情况,不超时。注意nex_permutation(a, a+k)函数的使用(使用do循环)。

代码:

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


int main()
{
    int T;
    string s;
    int a[15];
    int b, c;//b是大的那一方
    cin >> T;
    getchar();

    while(T--)
    {
        getline(cin, s);
        int k = 0, cnt;

        for(int i=0; i<s.size(); i++)
        {
            if(s[i]!=' ')
            {
                a[k++] = s[i]-'0';
            }
        }
        //要排好第一个排列(升序)
        sort(a, a+k);
        int Min = 1<<29;
        do
        {
            b = c = 0;
            if(a[0]==0 || (a[k/2]==0 && k>2)) continue;//舍去这个排列

            for(int i=0; i<k/2; i++)
                b = b*10+a[i];
            for(int i=k/2; i<k; i++)
                c = c*10+a[i];
            cnt = abs(b-c);
            Min = min(Min, cnt);
        }while(next_permutation(a, a+k));//获取下一个排序

        cout << Min << endl;
    }
    return 0;
}
Hopscotch(POJ 3050)

题目链接:Hopscotch

  • 题目大意:牛可以在一个矩阵上任意一点上向前后左右四个方向跳格子,跳五次后得到含有六个数字的数字串,计算其组成的整数,求所得整数的可能的个数。
  • 思路:穷竭搜索。注意使用set结构体来存储枚举得到的结果,这样可以省去很多代码。遍历每一个点起始得到的整数,存入Set,最后得到set的大小。
    代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;

const int MAX = 10;

struct Node
{
    int x, y;
    Node(int x=-1, int y=-1): x(x), y(y){}
};

int G[MAX][MAX];
int r[100000][MAX], n;
set<int> a;
Node b[MAX];
//右、左、下、上
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};

int cnt = 0;

void dfs(Node s, int step, int num)
{
    if(step==5)
    {
        a.insert(num);
        return;
    }

    for(int i=0; i<4; i++)
    {
        int x = dx[i]+s.x, y = dy[i]+s.y, t = step+1;
        if(x>=0 && x<5 && y>=0 && y<5)
        {
            dfs(Node(x, y), t, num*10+G[x][y]);
        }
    }
    return;
}

int main()
{
    n = 0;
    for(int i=0; i<5; i++)
    {
        for(int j=0; j<5; j++)
        {
            scanf("%d", &G[i][j]);
        }
    }

    for(int i=0; i<5; i++)
    {
        for(int j=0; j<5; j++)
        {
            dfs(Node(i, j), 0, G[i][j]);
        }
    }
    printf("%d\n", a.size());

    return 0;
}
贪心
Cleaning Shifts(POJ 2376)

题目链接:Cleaning Shifts

  • 题目大意:要求使用最少的区间覆盖一个大区间。
  • 思路:贪心。需要在可以首尾相接的条件下,保证得到的区间覆盖最远的距离。要注意首尾相接的条件可以不重合,但需要接上。

代码:

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

const int MAX = 25005;
struct Node
{
    int s, e;
    bool operator < (const Node &A) const
    {
        return s<A.s;
    }
};
Node c[MAX];

int main()
{
    int N, T;
    scanf("%d%d", &N, &T);
    for(int i=0; i<N; i++)
    {
        scanf("%d%d", &c[i].s, &c[i].e);
    }
    sort(c, c+N);
    int start = 0, end = 0, cnt = 0, flag, ans = 1;

    for(int i=0; i<N; i++)
    {
        flag = 0;
        //一定要注意是end+1
        if(c[i].s<=end+1 && c[i].e>cnt)//起始点可以接上,终点取满足起始点条件的最大值
        {
            cnt = c[i].e;
        }else if(c[i].s>end+1 && c[i].s<=cnt+1 && c[i].e>cnt)//有新的更新目标(即起始点接不上,但满足更新条件)
        {
            end = cnt;
            ans++;
            i--;//注意这种情况需要重来一次
        }
        if(cnt==T)//满足终止条件
        {
            flag = 1;
            break;
        }
    }

    if(flag)
        printf("%d\n", ans);
    else
        printf("-1\n");
    return 0;
}
Radar Installation(POJ 1328)

题目链接:Radar Installation
参考博文:Radar Installation

  • 题目大意:在坐标系中,x轴上方为海洋(海洋中有多个岛屿,以点标记),x轴下方为陆地。需要在陆地上建立多个雷达站,使得雷达探测范围可以将所有岛屿覆盖,求需要建立的雷达站的最少数目。
  • 思路:比较好的贪心算法题目。将思想转换一下,计算探测到每一个岛屿的雷达站建设区域,在重合区域内建立雷达站即可探测到多个岛屿。需要记录区域的起始和终止,按起始点从小到大排序,然后不断更新重叠区域的终止点(可能变近),如果没有重叠区域则加一(起始点比重叠区域的终止点还大)。

代码:

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

const int MAX = 1005;
struct Node
{
    double s, e;

    bool operator < (const Node &A) const
    {
        return s<A.s;
    }
};
Node a[MAX];
int n, d, x, y;

int main()
{
    int Case = 1;
    while(scanf("%d%d", &n, &d)!=EOF)
    {
        if(n==0 && d==0)
            break;
        int flag = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%d%d", &x, &y);
            if(y<=d)
            {
                double bias = sqrt(d*d-y*y);
                a[i].s = x-bias, a[i].e = x+bias;
            }
            else
            {
                flag = 1;
            }
        }
        printf("Case %d: ", Case++);
        if(flag)
        {
            printf("-1\n");
            continue;
        }
        //n不能是double类型,只能是int
        sort(a, a+n);
        int ans = 1;
        //注意使用double类型
        double start = a[0].s, end = a[0].e;
        for(int i=1; i<n; i++)
        {
            if(a[i].s>end)//没有交集
            {
                ans++;
                //start = a[i].s;
                end = a[i].e;
            }else if(a[i].e<=end)//交集缩小
            {
                //start = a[i].s;
                end = a[i].e;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
Stall Reservations(POJ 3190)

题目链接:Stall Reservations

  • 题目大意:每个牛在某个区间工作,需要占用一个牛棚,问至少需要准备多少牛棚,并给出一个分配方案。
  • 思路:贪心加上优先级队列的题。按照牛区间起始点从小到大排序并遍历,如果已经使用的牛棚的使用区间终止点比遍历的牛的区间起始点低,则该牛可以进入该牛棚工作,否则,新开一个牛棚。

代码:

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

const int MAX = 50005;
int N, s, t, ans[MAX];
struct Node
{
    int s, e, id;//起始点,终止点,编号(用来当下标)
    Node(int s = -1, int e = -1, int id = -1): s(s), e(e), id(id){}
};
bool cmp1(Node A, Node B)
{
    return A.s<B.s;
}
struct cmp2
{
    bool operator () (Node &A, Node &B)
    {
        return A.e>B.e;
    }
};

Node cows[MAX];
Node stall[MAX];
priority_queue<Node, vector<Node>, cmp2> q;//用来筛选stall(选择终止点最小的)

int main()
{
    while(scanf("%d", &N)!=EOF)
    {
        for(int i=0; i<N; i++)
        {
            scanf("%d%d", &cows[i].s, &cows[i].e);
            cows[i].id = i+1;
        }
        sort(cows, cows+N, cmp1);//起始点越靠前的越在前面
        int k = 1;
        ans[cows[0].id] = 1;//记录牛被分到了哪一个stall中

        q.push(Node(cows[0].s, cows[0].e, 1));
        for(int i=1; i<N; i++)
        {
            //比较最小终止点是否小于下一个起始点
            if(cows[i].s<=q.top().e)
            {
                k++;
                ans[cows[i].id] = k;
                q.push(Node(cows[i].s, cows[i].e, k));
            }else
            {
                Node t = q.top(); q.pop();
                t.e = cows[i].e;
                ans[cows[i].id] = t.id;
                q.push(t);
            }
        }
        printf("%d\n", k);
        for(int i=1; i<=N; i++)
        {
            printf("%d\n", ans[i]);
        }
        while(!q.empty()) q.pop();
    }

    return 0;
}
Yogurt factory(POJ 2393)

题目链接:Yogurt factory

  • 题目大意:有一个奶酪工厂,给出这个工厂每天加工每个奶酪需要的价格,以及每天的需求量,另外,奶酪也可以存放在仓库里(竟然放不坏!),给出每个奶酪存放一天需要的成本,问,这些生产任务全部完成,最少的花费是多少。
  • 思路:这道题的贪心策略比较巧妙。可以直接从前向后更新每一天的奶酪生产价格(包括存储成本)。即:更新当前周的价格:min(当前周价格,上一周的价格+S),更新后则直接计算即可。

代码:

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

typedef long long LL;
const int MAX = 10005;
int C[MAX], Y[MAX];
int N, S;
LL cost;

int main()
{
    scanf("%d%d", &N, &S);
    for(int i=0; i<N; i++)
        scanf("%d%d", &C[i], &Y[i]);
    cost = 0;
    //更新当前周的价格:min(当前周价格,上一周的价格+S)
    for(int i=1; i<N; i++)
    {
        C[i] = min(C[i], C[i-1]+S);
    }
    //计算值
    for(int i=0; i<N; i++)
    {
        cost += C[i]*Y[i];
    }
    printf("%lld\n", cost);
    return 0;
}
Packets(POJ 1017)

题目链接:Packets
参考博文:POJ1017 Packets(贪心算法训练)

  • 题目大意: 公司共有底面面积为11、22、33、44、55、66,高度同为H的六种产品,现在需要用最少的箱子打包,箱子的底面面积为6*6,高度为H。
  • 思路:先装大的后装小的,空隙之间可能装小的,尽量填满空隙。因此需要从大到小以此模拟装入,具体参考博文。

代码:

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

int a1, a2, a3, a4, a5, a6, s3, s2;

int main()
{
    while(scanf("%d%d%d%d%d%d", &a1, &a2, &a3, &a4, &a5, &a6)!=EOF)
    {
        if(a1+a2+a3+a4+a5+a6==0) break;
        int p = 0;
        p += a6/1;
        p += a5/1;
        a1 = max(a1-a5*11, 0);
        p += a4/1;
        if(a2<5*a4) a1 = max(a1-(5*a4-a2)*4, 0);
        a2 = max(a2-5*a4, 0);
        p += a3/4;
        s3 = a3%4;
        if(s3) p++;
        if(s3==1)
        {
            if(a2<5) a1 = max(a1-(5-a2)*4-7, 0);
            else     a1 = max(a1-7, 0);
            a2 = max(a2-5, 0);
        }
        else if(s3==2)
        {
            if(a2<3) a1 = max(a1-(3-a2)*4-6, 0);
            else     a1 = max(a1-6, 0);
            a2 = max(a2-3, 0);
        }
        else if(s3==3)
        {
            if(a2<1) a1 = max(a1-(1-a2)*4-5, 0);
            else     a1 = max(a1-5, 0);
            a2 = max(a2-1, 0);
        }
        p += a2/9;
        s2 = a2%9;
        if(s2!=0) p++;
        if(s2) a1 = max(a1-(36-4*s2), 0);
        p += (a1+35)/36;

        printf("%d\n", p);
    }
    return 0;
}
Allowance(POJ 3040)

题目链接:Allowance
参考博文:Allowance(POJ-3040)

  • 题目大意:每周至少给牛 c 元,有 n 种硬币,已知每种硬币币值和数量,求最多坚持的周数。
  • 思路:将硬币从小到大排序,大于等于 c 元的硬币直接支付,然后将不能直接支付的先从大到小凑到接近 c ,再从小到大向上加,如果能达到就记录并开始下一轮,如果不能就退出。要注意的是,硬币无法拆分,因此只能多给不能少给。

代码:

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

const int MAX = 1005;
const int INF = 1<<29;
struct Node
{
    int num, value;
    bool operator < (const Node &A) const
    {
        return value < A.value;
    }
};
int use[MAX];//记录硬币一周使用的个数
int n, c, cnt;

int main()
{
    cnt = 0;
    scanf("%d%d", &n, &c);

    for(int i=1; i<=n; i++)
        scanf("%d%d", &coin[i].value, &coin[i].num);

    sort(coin+1, coin+1+n);
    //找出硬币币值大于c元的数目
    for(int i=1; i<n; i++)
    {
        if(coin[i].value>=c)
        {
            cnt += coin[i].num;//直接支付
            coin[i].num = 0;//清零
        }
    }

    while(true)
    {
        bool flag = false;
        int sum = c;
        memset(use, 0, sizeof(use));

        //从大到小逼近c
        for(int i=n; i>0; i--)
        {
            if(coin[i].num>0)
            {
                use[i] = min(sum/coin[i].value, coin[i].num);
                sum -= use[i]*coin[i].value;
                if(sum==0)
                {
                    flag = true;
                    break;
                }
            }
        }

        //从小到大抵达或略微超过c
        if(sum>0)
        {
            for(int i=1; i<=n; i++)
            {
                if(coin[i].num-use[i]>0)
                {
                    while(use[i]<coin[i].num)
                    {
                        sum -= coin[i].value;
                        use[i]++;
                        if(sum<=0)
                        {
                            flag = true;
                            break;
                        }
                    }
                }
                if(flag) break;
            }
        }

        if(!flag) break;

        //计算上面得出的方案最多可以进行几周
        int minn = INF;
        for(int i=1; i<=n; i++)
        {
            if(use[i]>0)
                minn = min(minn, coin[i].num/use[i]);//记录所有硬币中最小的使用周数
        }

        for(int i=1; i<=n; i++)
        {
            if(use[i]>0)//按照求出的使用周数,更新硬币的数目
                coin[i].num -= minn*use[i];
        }
        cnt += minn;
    }
    printf("%d\n", cnt);

    return 0;
}
Stripies(POJ 1862)

题目链接:Stripies

  • 题目大意:从N个数任取两个数按2 × \times ×sqrt(a*b)合成新数放回,求最后那个数的最小值。
  • 思路:贪心策略是使尽量使大的数多参与开方运算。每次取出最大和次大的数字运算,直至剩下一个数字。因为运算公式的原因,所以运算之后的结果一定是最大的数字,所以可以简化计算,直接排序之后遍历计算。

代码:

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

const int MAX = 200;
bool cmp(int a, int b)
{
    return a>b;
}
int main()
{
    int n, m[MAX];
    cin >> n;
    for(int i=0; i<n; i++)
        cin >> m[i];
    sort(m, m+n, cmp);//从大到小排序

    double b = m[0];
    for(int i=1; i<n; i++)
    {
        b = 2*sqrt(b*m[i]);
    }
    printf("%.3f", b);
}
Protecting the Flowers(POJ 3262)

题目链接:Protecting the Flowers

  • 题目大意:n头牛在吃花,每头牛牵走(避免吃花)要花费2*t秒(来回),每头牛吃花的速率是d,要你自己设计一个牵走牛的顺序,使被牛吃的花最少。
  • 思路:贪心算法。其实就是按d/t从大到小排序,然后依次牵走。重点是可能超时,需要在代码优化上下点功夫。

代码:

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

typedef long long LL;
const int MAX = 100005;
struct Node
{
    int t, d;
};
bool cmp(Node a, Node b)
{
    double ap = a.d/(double)a.t;
    double bp = b.d/(double)b.t;
    return ap>bp;
}
Node C[MAX];
LL ans = 0, s = 0;

int main()
{
    int N, T, D;
    while(scanf("%d", &N)!=EOF)
    {
        for(int i=0; i<N; i++)
        {
            scanf("%d%d", &C[i].t, &C[i].d);
            s += C[i].d;//先加上,后减去,降低了复杂度
        }
        sort(C, C+N, cmp);

        for(int i=0; i<N-1; i++)
        {
            s -= C[i].d;
            ans += s*C[i].t*2;
        }
        printf("%lld\n", ans);
    }

    return 0;
}
动态规划
Sumsets(POJ 2229)

题目链接:Sumsets
参考博文:动态规划之划分数
DP:Sumsets(POJ 2229)

  • 题目大意:给一个数,将其进行分解,只能分解为2的幂次,最多能分解多少种。

代码:

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

const int MAX = 1000001;
int dp[MAX];
int c;

int main()
{
    int N;
    while(cin >> N)
    {
        dp[0] = 1;
        c = 1;
        for(int i=1; i<=20 && c<=N; i++)
        {
            for(int j=c; j<=N; j++)
            {
                dp[j] = (dp[j]+dp[j-c])%1000000000;
            }
            c = c*2;//累乘,节省了时间
        }
        cout << dp[N] << endl;
    }
    return 0;
}
Milking Time(POJ 3616)

题目链接:Milking Time
参考博文:poj 3616 Milking Time —DP(带权重的区间动态规划)

  • 题目大意:题意就是有m头牛,每一只牛有一个产奶的时间段和一个奶量,Bessie可以去给每一头奶牛挤奶,但是每次给一个奶牛挤奶之后必须休息R分钟,问Bessie选择怎么挤奶可以使挤出的奶最多。
  • 思路:
    • 状态构造:dp[i]代表在前i个区间可以获得的最大奶量。
    • 终态:dp[M],M是牛的数量。
    • 状态转移方程:dp[i] = max(dp[i-1], dp[p[i]]+w[i](其中p[i]表示在第i个区间之前最近的一个与第i个区间不冲突的区间的下标,w[i]表示第i个区间的奶量。
    • 初始化:dp[0] = 0;

代码:

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

const int MAX = 1000001;
struct Node
{
    int s, e, v;
    bool operator < (const Node &A) const
    {
        return e<A.e;
    }
};
Node C[1005];
int dp[1005];
int p[1005];//上一个不重合可使用区间
int N, M, R, start, end, eff;
bool check(Node a, Node b)
{
    if(a.s>=b.e+R || b.s>=a.e+R) return 1;
    else return 0;
}
void Calculate_P(int M, int R)
{
    p[1] = 0;
    for(int i=M; i>1; i--)
    {
        int k=i-1;
        while(k>0 && !check(C[k], C[i]))
            k--;
        p[i] = k;
    }
}
int main()
{
    while(scanf("%d%d%d", &N, &M, &R)!=EOF)
    {
        for(int i=1; i<=M; i++)
        {
            scanf("%d%d%d", &C[i].s, &C[i].e, &C[i].v);
        }
        sort(C+1, C+M+1);//按结束点排序
        Calculate_P(M, R);//计算每一个区间前面可以接着的最近区间下标
        memset(dp, 0, sizeof(dp));
        for(int i=1; i<=M; i++)
        {
            dp[i] = max(dp[i-1], dp[p[i]]+C[i].v);//第i个区间是否使用
        }
        printf("%d\n", dp[M]);
    }
    return 0;
}
数据结构
Sunscreen(POJ 3614)

题目链接:Sunscreen
参考博文:Sunscreen (poj 3614 贪心+优先队列)

  • 题目大意:有c头牛晒太阳,每头牛都有一个能承受辐射的范围(min~max),现在有 l 种防晒霜,每种防晒霜都能将辐射值固定在spf,每种防晒霜都有一定的数量num。每头牛用最多一种防晒霜,问最多能满足多少头牛。
  • 思路:先将防晒霜按照spf从小到大排序,再将牛按照min从小到大排序,最后按序取出防晒霜,如果牛的min小于等于spf则入队列,将所有满足条件的牛入队列,然后找出其max最小的牛且max>=spf。循环以上步骤直到防晒霜用完或牛遍历完。

代码:

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

const int MAX = 2505;
struct Sun
{
    int spf, num;
};
struct Cow
{
    int min, max;
};
struct cmp
{
    bool operator ()(Cow a, Cow b)
    {
        return a.max>b.max;
    }
};
bool cmp1(Cow a, Cow b)
{
    return a.min<b.min;
}
bool cmp2(Sun a, Sun b)
{
    return a.spf<b.spf;
}
priority_queue<Cow, vector<Cow>, cmp> Q;
Cow Cow[MAX], p;
Sun S[MAX];

int main()
{
    int C, L;
    cin >> C >> L;
    for(int i=0; i<C; i++)
        cin >> Cow[i].min >> Cow[i].max;
    for(int i=0; i<L; i++)
        cin >> S[i].spf >> S[i].num;
    sort(Cow, Cow+C, cmp1);
    Cow[C].min = 1<<29, Cow[C].max = 1<<29;//哨点
    sort(S, S+L, cmp2);
   
    int k = 0, ans = 0;
    for(int i=0; i<=C; i++)
    {
        if(k==L) break;
        if(Cow[i].min<=S[k].spf)
            Q.push(Cow[i]);
        else if(!Q.empty())//符合该防晒霜的牛已经全部进队列
        {
            p = Q.top();
            Q.pop();
            while(p.max<S[k].spf && !Q.empty())
            {
                p = Q.top(); Q.pop();
            }
            if(p.max>=S[k].spf)
            {
                S[k].num--;
                if(S[k].num==0)
                    k++;
                ans++;
            }
            i--;
        }
        else if(Q.empty())//该防晒霜不能用,换下一个
        {
            k++, i--;
        }
    }
    cout << ans << endl;
    return 0;
}
Moo University - Financial Aid(POJ 2010)

题目链接:Moo University - Financial Aid
参考博文:POJ 2010 Moo University – Financial Aid 题解 《挑战程序设计竞赛》

  • 题目大意:从C头奶牛中招收N头。它们分别得分score_i,需要资助学费aid_i。希望新生所需资助不超过F,同时得分中位数最高。求此中位数。
  • 思路:先将奶牛排序,考虑每个奶牛作为中位数时,比它分数低(前面的)的那群牛的学费总和lower_i(选取N/2个最少学费之和),后面的总和upper_i(选取N/2个最少学费之和)。然后从分数高往分数低扫描,满足aid_i + lower_i + upper_i <= F的第一个解就是最优解。

代码:

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

const int MAX = 100005;
struct Cow
{
    int s, m;
};
bool cmp(Cow a, Cow b)
{
    return a.s>b.s;
}
struct cmp1
{
    bool operator ()(Cow a, Cow b)
    {
        return a.m<b.m;
    }
};

int N, C, F;
Cow c[MAX];
int lower[MAX], upper[MAX];//在前i个或后i个牛中最少的N/2个学费之和
priority_queue<Cow, vector<Cow>, cmp1>q, p;//取最大的学费

int main()
{
    while(scanf("%d%d%d", &N, &C, &F)!=EOF)
    {
        for(int i=0; i<C; i++)
            scanf("%d%d", &c[i].s, &c[i].m);
        sort(c, c+C, cmp);//分数降序
        int n = N/2, total = 0;
        //求lower的值
        for(int i=0; i<C; i++)
        {
            if(q.size()>=n)
            {
                lower[i] = total;
            }else
            {
                lower[i] = 0;
            }
            q.push(c[i]);
            total += c[i].m;
            if(q.size()>n)
            {
                total -= q.top().m;
                q.pop();
            }
        }
        //求upper的值
        total = 0;
        for(int i=C-1; i>=0; i--)
        {
            if(p.size()>=n)
            {
                upper[i] = total;
            }else
            {
                upper[i] = 0;
            }
            p.push(c[i]);
            total += c[i].m;
            if(p.size()>n)
            {
                total -= p.top().m;
                p.pop();
            }
        }
        //筛选出最终结果
        int ans = -1;
        for(int i=C; i>=0; i--)
        {
            if(lower[i]+c[i].m+upper[i]<=F)//判断条件
            {
                ans = c[i].s;
                break;
            }
        }
        if(ans==-1)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}
并查集
Wireless Network

题目链接:Wireless Network

  • 题目大意:一些结点需要维修,并检查一些结点是否连通。给出结点数目,结点之间的连通半径,并给出所有结点的坐标,最后给出相应操作,维修和检验联通,每一次检验均需要给出检验结果。
  • 思路:并查集题。在维修时,判断该结点是否可以与之间维修的结点并入一个集合(即连通),检验时直接用并查集检验。

代码:

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

const int MAX = 1005;
struct Node
{
    int x, y;
};
Node C[MAX];
int a[MAX], d, N, id, id2;
int pre[MAX], Rank[MAX];
char order;
bool check(int x1, int y1, int x2, int y2)
{
    if(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<=(double)d)
    {
        return true;
    }
    return false;
}

void Init()
{
    for(int i=0; i<MAX; i++)
    {
        pre[i] = i;
        Rank[i] = 1;
    }
}
int findRoot(int x)
{
    int r = x;
    while(r!=pre[r])
    {
        r = pre[r];
    }
    int j=x, i;
    while(j!=r)
    {
        i = pre[j];
        pre[j] = r;
        j = i;
    }
    return r;
}
void join(int x, int y)
{
    int fx = findRoot(x), fy = findRoot(y);
    if(Rank[fx]>Rank[fy])
    {
        pre[fy] = fx;
    }else
    {
        pre[fx] = fy;
        if(Rank[fx]==Rank[fy]) Rank[fy]++;
    }
}
bool same(int x, int y)
{
    return findRoot(x)==findRoot(y);
}

int main()
{
    scanf("%d%d", &N, &d);
    Init();
    for(int i=1; i<=N; i++)
    {
        scanf("%d%d", &C[i].x, &C[i].y);
    }
    int k = 0;
    while(scanf("%c%d", &order, &id)!=EOF)
    {
        if(order=='O')
        {
            a[k++] = id;
            for(int i=0; i<k-1; i++)
            {
                if(check(C[a[i]].x, C[a[i]].y, C[id].x, C[id].y))
                {
                    join(id, a[i]);
                }
            }
        }
        else if(order=='S')
        {
            scanf("%d", &id2);
            if(same(id, id2))
            {
                //cout << pre[id] << " " << pre[id2] << endl;
                printf("SUCCESS\n");
            }else
            {
                printf("FAIL\n");
            }
        }
    }
    return 0;
}
Find them, Catch them(POJ 1703)

题目链接:Find them, Catch them

  • 题目大意:在一个城市里有两种不同的犯罪团伙。首先输入T表示有T组测试,然后输入N和M,表示有N个罪犯(编号从1到N)而且接下来有M个操作。操作分为两种:
    1.D a b,表示编号为a和b的两个罪犯属于不同的犯罪团伙;
    2.A a b,表示询问编号为a和b的两个罪犯是否是同一个犯罪团伙或者不确定。
    对于每一个A操作,根据题意都要有相应的回答(输出)。
  • 思路:这道题目类似食物链的题目食物链相关讲解),属于种类并查集,即无法准确确定一个类具体属于那一个种类,只能知道不同类之间的关系,这样就需要维护比原始数目大种类个数倍的数组,同时更新每一个每一个阶段的数组,维护其关系而不是维护其种类。
    本题目的并查集表示A和B分别属于两个集团的关系同时存在。

代码:

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

const int MAX = 100005;
int pre[MAX*2], Rank[MAX*2];
int N = MAX-1;

//并查集表示关系同时存在
void Init()
{
    for(int i=0; i<=2*MAX; i++)
    {
        pre[i] = i;
        Rank[i] = 1;
    }
}
int findRoot(int x)
{
    int r = x;
    while(r!=pre[r])
    {
        r = pre[r];
    }
    int j=x, i;
    while(j!=r)
    {
        i = pre[j];
        pre[j] = r;
        j = i;
    }
    return r;
}
void join(int x, int y)
{
    int fx = findRoot(x), fy = findRoot(y);
    if(Rank[fx]>Rank[fy])
    {
        pre[fy] = fx;
    }else
    {
        pre[fx] = fy;
        if(Rank[fx]==Rank[fy]) Rank[fy]++;
    }
}
bool same(int x, int y)
{
    return findRoot(x)==findRoot(y);
}

int main()
{
    int T, N, M, a, b;
    char order;
    scanf("%d", &T);
    while(T--)
    {
        Init();
        scanf("%d%d", &N, &M);
        for(int i=0; i<M; i++)
        {
            getchar();
            scanf("%c%d%d", &order, &a, &b);
            if(order=='D')
            {
                join(a, b+N);
                join(a+N, b);
            }else if(order=='A')
            {
                if(same(a, b))
                    printf("In the same gang.\n");
                else if(same(a, b+N) || same(a+N, b))//不是一个种类
                    printf("In different gangs.\n");
                else
                    printf("Not sure yet.\n");

            }
        }
    }
    return 0;
}
图论
Six Degrees of Cowvin Bacon(POJ 2139)

题目链接:Six Degrees of Cowvin Bacon

  • 题目大意:先根据题意建立图,然后求出每个点到其他每个点的最短距离的均值(即总和/可以到达的点的个数)
  • 思路:使用的多源最短路径中的Floyd算法。得到了所有结点之间的最短距离后,遍历每一个点的均值,然后得到最小的值就是结果。

代码:

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

const int MAX = 500;
const int INF = 1<<29;
int G[MAX][MAX], a[MAX];
int cnt = 0, Min = INF, ans;
int N, M, n;

int main()
{
    scanf("%d%d", &N, &M);
    for(int i=0; i<=N; i++)
    {
        for(int j=0; j<=N; j++)
            G[i][j] = INF;
    }
  
    while(M--)
    {
        scanf("%d", &n);
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            for(int j=i-1; j>=0; j--)
            {
                G[a[j]][a[i]] = 1;
                G[a[i]][a[j]] = 1;
            }
        }
    }
    
    for(int k=1; k<=N; k++)
    {
        for(int i=1; i<=N; i++)
        {
            if(G[i][k]==INF) continue;
            for(int j=1; j<=N; j++)
            {
                if(G[k][j]==INF) continue;
                G[i][j] = min(G[i][j], G[i][k]+G[k][j]);
            }
        }
    }
    Min = INF;
    double r;
    for(int i=1; i<=N; i++)
    {
        cnt = 0, ans = 0;
        for(int j=1; j<=N; j++)
        {
            if(i==j) continue;
            if(G[i][j]!=INF)
            {
                ans++;
                cnt += G[i][j];
            }
        }
        Min = min(Min, cnt);
        //cout << cnt << endl;
        r = Min/(1.0*ans)*100;
    }
    Min = r;
    printf("%d\n", Min);
    return 0;
}
Wormholes(POJ 3259)

题目链接:Wormholes

  • 题目大意:农夫john发现了一些虫洞,虫洞是一种在你到达虫洞之前把你送回目的地的一种方式,FJ的每个农场,由n块土地(编号为1-n),M条路,和W个 虫洞组成,FJ想从一块土地开始,经过若干条路和虫洞,返回到他最初开始走的地方并且时间要在他离开之前,或者恰好等于他离开的时间。
  • 思路:明显的使用带负权的单源最短路径算法,所有选择Bellman_Ford算法,最后判断一下是否有负环而已。裸题。

代码:

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

const int MAX = 505;
const int INF = 1<<29;
int G[MAX][MAX], d[MAX];
int F, N, M, W, En, S, E, T;
struct Edge
{
    int from, to, w;
}e[5005];
void Bellman_Ford(int s)
{
    fill(d, d+MAX, INF);
    d[s] = 0;
    int k = 0;
    while(1)
    {
        int flag = 0;
        for(int i=0; i<En; i++)
        {
            if(d[e[i].from]!=INF && d[e[i].to]>d[e[i].from]+e[i].w)
            {
                d[e[i].to] = d[e[i].from]+e[i].w;
                flag = 1;
            }
        }
        if(!flag) break;
        k++;
        if(k>=N) break;
    }
    if(k>=N)
        printf("YES\n");
    else
        printf("NO\n");
}
int main()
{
    scanf("%d", &F);
    while(F--)
    {
        En = 0;
 
        scanf("%d%d%d", &N, &M, &W);
        for(int i=0; i<M; i++)//双向
        {
            scanf("%d%d%d", &S, &E, &T);
            e[En].w = T, e[En].from = S, e[En++].to = E;
            e[En].w = T, e[En].from = E, e[En++].to = S;
        }
        for(int i=0; i<W; i++)//单向
        {
            scanf("%d%d%d", &S, &E, &T);
            e[En].w = -T, e[En].from = S, e[En++].to = E;
        }
        Bellman_Ford(1);
    }
    return 0;
}
Silver Cow Party(POJ 3268)

题目链接:Silver Cow Party

  • 题目大意:给定一些路线和花费,然后N头牛,每头牛要求按照最短路走到X处,并用最短路在返回,问这些牛中所有路线里最大值是多少。(注意是单向图)
  • 思路:可以先从X用Dijkstra,再将图的边的方向反过来,再用dijstra求一下X的单源最短路径,求二者和最大即可

代码:

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

const int MAX = 1005;
const int INF = 1<<29;
int G[MAX][MAX];
int d[MAX], d2[MAX], vis[MAX];
int N, M, X, A, B, T;

void dijstra(int s, int (&d)[MAX])
{
    fill(d, d+N+2, INF);
    memset(vis, 0, sizeof(vis));
    d[s] = 0;

    while(1)
    {
        int minv = INF, u;
        for(int i=1; i<=N; i++)
        {
            if(vis[i]==0 && d[i]<minv)
            {
                minv = d[i];
                u = i;
            }
        }
        if(minv==INF) break;
        vis[u] = 1;

        for(int j=1; j<=N; j++)
        {
            if(vis[j]==0 && G[u][j]!=INF && d[u]+G[u][j]<d[j])
            {
                d[j] = G[u][j] + d[u];
            }
        }
    }
}

int main()
{
    scanf("%d%d%d", &N, &M, &X);

    for(int i=0; i<=N; i++)
    {
        for(int j=0; j<=N; j++)
        {
            G[i][j] = INF;
        }
    }
    for(int i=0; i<M; i++)
    {
        scanf("%d%d%d", &A, &B, &T);
        G[A][B] = T;
    }

    dijstra(X, d);

    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=i; j++)
        {
            swap(G[i][j], G[j][i]);
        }
    }
    dijstra(X, d2);

    int s = 0;
    for(int i=1; i<=N; i++)
    {
        if(d[i]!=INF && d2[i]!=INF)
            s = max(d[i]+d2[i], s);
    }

    printf("%d\n", s);
    return 0;
}
Bad Cowtractors(POJ 2377)

题目链接:Bad Cowtractors

  • 题目大意:给定每条路线间需要的费用,建造一个最贵的网络线路(任意两节点都可以互相达到,但是不存在回路),求最多需要花费多少钱。
  • 思路:求最大生成树。则将边的权重取负,然后按照最小生成树计算即可。注意:使用邻接表可以方便处理结点之间有重边的情况,并且效率还高,推荐使用邻接表实现。

代码:

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

/*
使用了邻接表,不需要考虑重边,因为所以的边均保存了;
如果是邻接矩阵,需要保存最大或最小的边
*/
const int MAX = 1005;
const int INF = 1<<29;
int N, M;
int A, B, C;
int vis[MAX], d[MAX];
struct Node
{
    int to, w;
    Node(int to=-1, int w=-1): to(to), w(w){}
};
vector<Node>G[MAX];

void Prim()
{
    fill(d, d+MAX, INF);
    memset(vis, 0, sizeof(vis));
    d[1] = 0;
    while(1)
    {
        int Minv = INF, u;
        for(int i=1; i<=N; i++)
        {
            if(vis[i]==0 && d[i]<Minv)
            {
                Minv = d[i];
                u = i;
            }
        }
        if(Minv==INF) break;
        vis[u] = 1;
        //cout << u << " " << d[u] << endl;
        for(int j=0; j<G[u].size(); j++)
        {
            Node v = G[u][j];
            if(vis[v.to]==0 && d[v.to]>v.w)
            {
                d[v.to] = v.w;
            }
        }
    }
    int sum = 0, flag = 0;
    //cout << endl;
    for(int i=1; i<=N; i++)
    {
        if(d[i]!=INF)
            sum += d[i];
        else
            flag = 1;
    }
    if(!flag)
        printf("%d\n", -sum);
    else
        printf("-1\n");
}
int main()
{
    cin >> N >> M;
    for(int i=0; i<M; i++)
    {
        cin >> A >> B >> C;
        G[A].push_back(Node(B, -C));
        G[B].push_back(Node(A, -C));
    }
    Prim();
    return 0;
}
Out of Hay(POJ 2395)

题目链接:Out of Hay

  • 题目大意:有n个农场,贝西在1号农场,要访问其他n-1个农场,给出m条路,a b c表示a农场到b农场路程为c(两个农场间可能有多条路)。贝西会挑选最短的路径来访问完这n-1个农场。 问在此过程中贝西会经过的最大边是多大?
  • 思路:求最小生成树上的最大边,注意有重边,使用邻接表。

代码:

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

/*
使用了邻接表,不需要考虑重边,因为所以的边均保存了;
如果是邻接矩阵,需要保存最大或最小的边
*/
const int MAX = 2005;
const int INF = 1<<29;
int N, M;
int A, B, C;
int vis[MAX], d[MAX];
struct Node
{
    int to, w;
    Node(int to=-1, int w=-1): to(to), w(w){}
};
vector<Node>G[MAX];

void Prim()
{
    fill(d, d+MAX, INF);
    memset(vis, 0, sizeof(vis));
    d[1] = 0;
    while(1)
    {
        int Minv = INF, u;
        for(int i=1; i<=N; i++)
        {
            if(vis[i]==0 && d[i]<Minv)
            {
                Minv = d[i];
                u = i;
            }
        }
        if(Minv==INF) break;
        vis[u] = 1;
        //cout << u << " " << d[u] << endl;
        for(int j=0; j<G[u].size(); j++)
        {
            Node v = G[u][j];
            if(vis[v.to]==0 && d[v.to]>v.w)
            {
                d[v.to] = v.w;
            }
        }
    }
    int Max = 0;
    //cout << endl;
    for(int i=1; i<=N; i++)
    {
        Max = max(Max, d[i]);
    }
    printf("%d\n", Max);
}
int main()
{
    cin >> N >> M;
    for(int i=0; i<M; i++)
    {
        cin >> A >> B >> C;
        G[A].push_back(Node(B, C));
        G[B].push_back(Node(A, C));
    }
    Prim();
    return 0;
}
数论
Dead Fraction(POJ 1930)

题目链接:Dead Fraction
参考博文:poj 1930 Dead Fraction
参考博文:POJ - 1930 Dead Fraction(简单数学推理)

Prime Path(POJ 3126)

题目链接:Prime Path

  • 题目大意:给你两个四位数m和n,求m变到n至少需要几步;每次只能从个十百千上改变一位数字,并且改变后的数要是素数。
  • 思路:使用埃氏筛法来筛选出给定范围内的所有素数,然后使用BFS搜索到达n的最短路径,使用素数数组来作为判定减枝条件。

代码:

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

const int MAX = 10000;
int isPrime[MAX];
int vis[MAX];
int T, from, to;
struct Node
{
    int n, t;//n是数字,t是步数
    Node(int n=-1, int t=-1): n(n), t(t){}
};
queue<Node> q;
void getPrime()
{
    fill(isPrime, isPrime+MAX, 1);
    isPrime[1] = 0;
    for(int i=2; 2*i<MAX; i++)
    {
        if(isPrime[i])
        {
            for(int j=2*i; j<MAX; j+=i)
            {
                isPrime[j] = 0;
            }
        }
    }
}

void bfs(int from)
{
    while(!q.empty()) q.pop();
    memset(vis, 0, sizeof(vis));
    q.push(Node(from, 0));
    vis[from] = 1;
    while(!q.empty())
    {
        Node u = q.front(); q.pop();

        if(u.n==to)
        {
            printf("%d\n", u.t);
            return;
        }
        int k = 1;
        for(int i=0; i<4; i++)
        {
            for(int j=0; j<10; j++)//注意从0开始
            {
                if(i==3 && j==0) continue;//首位不能是0
                int v = u.n/(k*10)*(k*10)+(u.n%k)+j*k;//组合成新的数字
                if(isPrime[v] && vis[v]==0)//判定减枝
                {
                    q.push(Node(v, u.t+1));
                    vis[v] = 1;
                }
            }
            k *= 10;
        }
    }
    printf("Impossible\n");
}

int main()
{
    scanf("%d", &T);
    getPrime();
    while(T--)
    {
        scanf("%d%d", &from, &to);
        bfs(from);
    }
    return 0;
}
X-factor Chains(POJ 3421)

题目链接:X-factor Chains
参考博文:POJ 3421 X-factor Chains (因式分解+排列组合)

  • 题目大意:一条整数链,要求相邻两数前一个整除后一个。给出链尾的数,求链的最大长度以及满足最大长度的不同链的数量。
  • 思路:因式分解的素因子个数即为链长。因为链中后一个数等于前一个数乘以某素因子(相当于1乘以一些素数然后得到X),所以链的数量即为这些素因子不相异的全排列数:A!/(a1!a2!a3!..)

代码:

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

const int MAX = 1<<20+1;
typedef long long LL;
LL X, p[MAX], a[MAX];


int main()
{
    while(scanf("%lld", &X)!=EOF)
    {
        int cnt = 0;
        //素因子分解(因为从小到大分解,得到的一定是素数)
        for(LL i=2; i*i<=X; i++)
        {
            if(X%i==0)
            {
                p[cnt] = i;
                a[cnt++] = 1;
                X /= i;
            }
            while(X%i==0)
            {
                a[cnt-1]++;
                X /= i;
            }
        }
        if(X>1)//这一步容易遗忘
        {
            p[cnt] = X;
            a[cnt++] = 1;
        }
        //相关计算
        LL A = 0, ans = 1;
        for(int i=0; i<cnt; i++)
        {
            A += a[i];
        }
        for(LL i=2; i<=A; i++)
        {
            ans *= i;
        }
        for(LL i=0; i<cnt; i++)
        {
            for(LL j=2; j<=a[i]; j++)
            {
                ans /= j;
            }
        }
        printf("%lld %lld\n", A, ans);
    }
    return 0;
}
Semi-prime H-numbers(POJ 3292)

题目链接:Semi-prime H-numbers
参考博文:Semi-prime H-numbers POJ - 3292(简单打表)

  • 题目大意:定义一种数叫H-numbers,它是所有能除以四余一的数。
    在H-numbers中分三种数:
    1、H-primes,这种数只能被1和它本身整除,不能被其他的H-number整除,例如9是一个H-number,能被1,3,9整除,但3不是H-number,所以他是H-primes。
    2、H-semi-primes是由两个H-primes相乘得出的,H-semi-prime只能分解成两个H-prime数相乘。
    3、剩下的是H-composite。
    问给一个数,求1到这个数之间有多少个H-semi-primes。
  • 思路:打表得出所有H-semi-primes,然后使用数组累积得到输出结果。我的方法有点麻烦,虽然思想是一样的,还是代码能力不行。

先看别人的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define MAXN ((int)1e6 + 10)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int vis[MAXN];
int ans[MAXN];
void prime_()
{
    for(int i = 5; i <= MAXN; i += 4)
    {
        for(int j = 5; j <= MAXN; j += 4)
        {
            if(i * j > MAXN) break;//超出范围
         	//i 与 j 都不可以被分解,换句话说就是之前没有被构造出来过
            //i*j一定符合4*n+1,这时的i*j定是H-semi—prime,以后再以i * j作为因子的数就不可以构成了,仔细体会下
            if(!vis[i] && !vis[j]) vis[i * j] = 1;//得到H-semi—prime
    		//之前被构造出来过 所以 i * j 可以被分解成至少三个H-prime,所以不行
            else vis[i * j] = -1; 
        }
    }
 
    int len = 0;//累积
    for(int i = 1; i <= MAXN; i++){ //打表,计算出1-i之间的多少个H-semi—prime数
        if(vis[i] == 1) len++;
        vis[i] = len; //有点像前缀和
    }
}
int main()
{
    int n;
    prime_();
    while(~scanf("%d", &n) && n)
    {
        printf("%d %d\n", n, vis[n]);
    }
}

自己的代码:

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

typedef long long LL;
const int MAX = 1000002;
int h, flag[MAX], ans[MAX];
vector<int> v;
int r[MAX];

//打表+筛选
void isHPrime()
{
    memset(flag, 1, sizeof(flag));
    //得到H素数,存入数组,并将H-composite标记为0
    for(int i=1; i<=MAX/4; i++)
    {
        int j = 4*i+1;
        if(j>=MAX) break;
        if(flag[j])
        {
            for(int k=2; k<MAX; k++)
            {
                if((k*j-1)%4!=0) continue;
                int a = (k*j-1)/4;
                if(4*a+1>=MAX) break;
                flag[4*a+1] = 0;
            }
            v.push_back(j);
        }
    }

    //标记所有的H_s_p
    int k = 0;
    r[k++] = 0;//作为一个初始数据方便下面的程序
    for(int i=0; i<v.size(); i++)
    {
        for(int j=i; j<v.size(); j++)
        {
            if(MAX/v[i]<v[j]) break;//很重要,不然程序会溢出
            LL u = v[i]*v[j];
            if(u<MAX && flag[u]==0)//乘积是H-composite
            {
                r[k++] = u;
            }
            else
                break;
        }
    }
    sort(r, r+k);//排序,方便下面的程序

    //更新数量数组
    ans[0] = 0;
    for(int i=1; i<k; i++)//r的下标
    {
        for(int j=r[i-1]+1; j<r[i]; j++)//相邻r元素之间的数据赋同样的值
        {
            ans[j] = ans[j-1];
        }
        ans[r[i]] = ans[r[i]-1]+1;//加一
    }
}
int main()
{
    isHPrime();
    while(scanf("%d", &h)!=EOF && h)
    {
        printf("%d %d\n", h, ans[h]);
    }
    return 0;
}
Raising Modulo Numbers(POJ 1995)

题目链接:Raising Modulo Numbers

  • 题目大意:(A1^B1 +A2^B2+ … +AH^BH)mod M。
  • 思路:快速幂乘,只不过每乘一次或加一次都需要模M一次。

代码:

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

typedef long long LL;
LL Z, H, M, A, B;

LL Pow(LL a, LL p)
{
    LL ans = 1, t = p;
    while(t)
    {
        if(t&1) ans = (a*ans)%M;
        a = (a*a)%M;
        t >>= 1;
    }
    return ans;
}

int main()
{
    scanf("%lld", &Z);
    while(Z--)
    {
        scanf("%lld%lld", &M, &H);
        LL cnt = 0;
        for(LL i=0; i<H; i++)
        {
            scanf("%lld%lld", &A, &B);
            cnt = (cnt+Pow(A, B))%M;
        }
        printf("%lld\n", cnt);
    }
    return 0;
}
Pseudoprime numbers(POJ 3641)

题目链接:Pseudoprime numbers

  • 题目大意:给出 p 和 a,若 a^p 和a对 p 同余且 p 不是素数,则输出 yes,否则输出 no。
  • 思路:快速幂乘,且判断素数。

代码:

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

typedef long long LL;
LL p, a;

int IsPrime(LL p)
{
    for(LL i=2; i*i<=p; i++)
    {
        if(p%i==0)
            return 0;
    }
    return 1;
}
LL Pow(LL a, LL p)
{
    LL ans = 1, t = p;
    while(t)
    {
        if(t&1) ans = (a*ans)%p;
        a = (a*a)%p;
        t >>= 1;
    }
    return ans;
}
void solve(LL a, LL p)
{
    if(IsPrime(p)||Pow(a, p)!=a%p)
        printf("no\n");
    else
        printf("yes\n");
}

int main()
{
    while(scanf("%lld%lld", &p, &a)!=EOF && (a+p))
    {
        solve(a, p);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值