挑战程序设计竞赛(第二章:2.4 数据结构)

文章目录

          • Expedition
          • 食物链

Expedition

参考博文:挑战程序设计竞赛:Expeition

  • 思路:需要在能够达到的加油站中选取存油最多的加油站加油,所有使用优先队列保存可以达到的加油站,然后取油量最大的加油,然后扩展车辆行驶距离,再在范围内选择油量最大的加油站。如果优先队列为空时,车还没有到达目的地,则表明车辆无法到达目的地。
  • 记忆:对于自定义的sort比较大小函数,>表示降序;对于自定义的优先队列比较大小函数,>表示取最小。
#include 
#include 
#include 
#include 
using namespace std;

const int MAX = 10001;
int N, L, P, sd, sf;

struct Node
{
    int dis, fuel;
    /* bool operator < (const Node& B) const { return dis > B.dis; }*/
};

bool cmp1(Node a, Node b)
{
   return a.dis > b.dis;//降序排列
}

struct cmp2
{
    bool operator()(Node &a, Node &b)
    {
        return a.fuel < b.fuel;//取最大
    }
};
Node S[MAX];
priority_queue<Node, vector<Node>, cmp2>q;

int main()
{
    while(scanf("%d", &N)!=EOF)
    {
        for(int i=0; i<N; i++)
        {
            scanf("%d%d", &S[i].dis, &S[i].fuel);
        }
        scanf("%d%d", &sd, &sf);
        sort(S, S+N, cmp1);

        //end是车能够到达距离目的地最近的距离,cnt是最大可以行进的距离,k标记下一个遍历的加油站编号,ans是停止次数
        int end = sd-sf, cnt = sf, k = 0, ans = 0, i;

        while(1)
        {
            //如果能够到达目的地
            if(cnt >= sd) break;
            for(i=k; i<N; i++)
            {
                //将能够到达的加油站全部入队列
                if(S[i].dis>=end)
                    q.push(S[i]);
                else
                {
                    k = i;
                    break;
                }
            }
            //如果没有加油站可以加油且还没有到达目的地
            if(q.empty()) break;

            //选取可以到达的加油站中油量最多的一个
            cnt += q.top().fuel;
            end = end-q.top().fuel;
            q.pop();
            ans++;
        }
        //cout << cnt << endl;
        if(cnt>=sd) printf("%d\n", ans);
        else        printf("-1\n");
    }
    return 0;
}
食物链

参考博文:poj-1182 食物链

  • 思路:大坑题,测试数据只有一组,不能循环输入。不是简单的并查集(至于要判断是否是在同一个集合中),还需要判断多种关系,判断是否前后矛盾。
    因此,思想是使用并查集来表示在同一个集合中是可以同时存在的关系,并且不指定该动物是属于那一个类型,同时维护三种类型。有三个类型,因此扩容数组为3*N,其中1-N代表A类型,N+1-2xN代表B类型,2xN+1-3xN代表C类型。一个集合中可能存在1,2+N,表示当编号为1的动物类型为A时,编号为2的动物类型为B。
  • 思考:算法采用扩充数组的方法实际上是因为无法确定任何一个动物是属于哪一种类型,所有需要保存它所有可能的状态,防止出现冲突。比如输入 2 1 4表示1号吃4号,如果确定1号为A类型,4号为B类型,那么在输入 2 3 5将无法确定3和5的类型,因为不知道其与1和4的关系,所有最简便的方法就是保存所有可能的类型来维护其之间的关系,然后更新时同时更新三个数组部分。

代码:

#include 
#include 
using namespace std;

const int MAX = 1e6+5;
int N, K, D, X, Y;
int pre[MAX];
int Rank[MAX];
/* 在同一个并查集中的表示类型关系同时存在,0-N代表A类型,N-2*N代表B类型, 2*N-3*N代表C类型 */
void Init(int n)
{
    for(int i=0; i<=n; i++)
    {
        pre[i] = i;
        Rank[i] = 0;
    }
}
int findRoot(int x)
{
    int r = x;
    //寻找根节点
    while(pre[r]!=r)
    {
        r = pre[r];
    }
    //路径压缩
    int j=x, i;
    while(pre[j]!=r)
    {
        i = j;
        pre[j] = r;
        j = pre[i];
    }
    return r;
}
void join(int x, int y)
{
    int fx = findRoot(x), fy = findRoot(y);
    if(fx==fy) return;
    if(Rank[fx]<Rank[y])
    {
        pre[fx] = fy;
    }
    else
    {
        pre[fy] = fx;
        if(Rank[fx]==Rank[fy]) Rank[fx]++;
    }
}
bool same(int x, int y)
{
    return findRoot(x) == findRoot(y);
}

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

    int cnt = 0;
    Init(3*N);//初始化:准备3*N个节点表示n个元素
    for(int i=0; i<K; i++)
    {
        scanf("%d%d%d", &D, &X, &Y);
        if(X>N || Y>N || X<=0 || Y<=0)//判断b和c是否在范围里面
        {
            cnt++;
            continue;
        }
        if(D==1)//x和y属于同一类的信息
        {
            if(same(X, Y+2*N) || same(X, Y+N))//判断b和c在之前的情况中,是否已构成捕食与被捕食的关系
                cnt++;
            else//如果没有捕食关系,则在3个区间中把b和c放在一起
            {
                join(X, Y);
                join(X+N, Y+N);
                join(X+2*N, Y+2*N);
            }
        }
        else//x吃y的信息
        {
            if(same(X, Y) || same(X, Y+2*N))//判断b和c是否为同一类或者已经构成c吃b的捕食关系,若是则为错ans+1
                cnt++;
            else
            {
                join(X, Y+N);
                join(X+N, Y+2*N);
                join(X+2*N, Y);
            }
        }
    }
    printf("%d\n", cnt);
    return 0;
}

你可能感兴趣的:(挑战程序设计竞赛——经验篇)