并查集

来源:互联网 发布:c语言split函数 编辑:程序博客网 时间:2024/06/05 09:58
树的判定
时间限制:1000 ms  |  内存限制:65535 KB
难度:4
描述
A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties. 

There is exactly one node, called the root, to which no directed edges point. 
Every node except the root has exactly one edge pointing to it. 
There is a unique sequence of directed edges from the root to each node. 

For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not. 
In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.
输入
The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero.

The number of test cases will not more than 20,and the number of the node will not exceed 10000.
The inputs will be ended by a pair of -1.
输出
For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).
样例输入
6 8  5 3  5 2  6 4 5 6  0 0

8 1  7 3  6 2  8 9  7 5 7 4  7 8  7 6  0 0

3 8  6 8  6 4 5 3  5 6  5 2  0 0
-1 -1
样例输出
Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.

题意:给出节点与节点之间的关系(单向),判断此图是否为树

代码一:(并查集的使用)

#include <stdio.h>  #include <memory.h>    int up[10005];//父亲节点int down[10005];//孩子节点int father[10005];    int find(int x)  {      if (father[x] == -1)      {          return x;      }      return find(father[x]);  }    void unionset(int x, int y)  //合并两个不相交的集合{      father[y] = x;  }    int main()  {      int x, y;      int count, tc = 1; //tc标记第几组数     while (1)      {          memset(father, -1, sizeof(father));          memset(up, 0, sizeof(up));          memset(down, 0, sizeof(down));          scanf("%d%d", &x, &y);          if (x==-1 && y == -1)  //结束程序        {              break;          }          if ((x || y) == 0)          {              printf("Case %d is a tree.\n", tc++);              continue;          }          count = 0;          up[count] = x;          down[count] = y;          while (scanf("%d%d", &x, &y), x&&y)          {              count++;              up[count] = x;              down[count] = y;          }          bool flag = true; //flag用来标记是否可以成树         for (int j=0; j<=count; ++j) // 判断环,入度为1          {              x = find(up[j]);            // 查找father              y = find(down[j]);          // 查找father              if (x==y || y != down[j])                 // (1)up[j]和down[j]都没有father 相等自身形成环             // (2)father(x=y)相同(如果联合x,y,就会形成环                          // (3)y已经有father(判断入度是否为1(树入度为1))             {                  flag = false;                  break;              }              unionset(x, y);          }          if (!flag)          {              printf("Case %d is not a tree.\n", tc++);              continue;          }          x = find(down[0]);                        flag = true;          for (int i=1; i<=count; ++i)     //判断是不是一颗树(根节点是否相同)          {              if (x != find(down[i]))              {                  flag = false;                  break;              }          }          if (flag)          {              printf("Case %d is a tree.\n", tc++);          }          else          {              printf("Case %d is not a tree.\n", tc++);          }      }      return 0;  }  
Supermarket(nyoj208)
时间限制:1000 ms  |  内存限制:65535 KB
难度:4
描述
A supermarket has a set Prod of products on sale. It earns a profit px for each product x∈Prod sold by a deadline dx that is measured as an integral number of time units starting from the moment the sale begins. Each product takes precisely one unit of time for being sold. A selling schedule is an ordered subset of products Sell ≤ Prod such that the selling of each product x∈Sell, according to the ordering of Sell, completes before the deadline dx or just when dx expires. The profit of the selling schedule is Profit(Sell)=Σx∈Sellpx. An optimal selling schedule is a schedule with a maximum profit. 
For example, consider the products Prod={a,b,c,d} with (pa,da)=(50,2), (pb,db)=(10,1), (pc,dc)=(20,2), and (pd,dd)=(30,1). The possible selling schedules are listed in table 1. For instance, the schedule Sell={d,a} shows that the selling of product d starts at time 0 and ends at time 1, while the selling of product a starts at time 1 and ends at time 2. Each of these products is sold by its deadline. Sell is the optimal schedule and its profit is 80. 
Write a program that reads sets of products from an input text file and computes the profit of an optimal selling schedule for each set of products. 
输入
A set of products starts with an integer 0 <= n <= 10000, which is the number of products in the set, and continues with n pairs pi di of integers, 1 <= pi <= 10000 and 1 <= di <= 10000, that designate the profit and the selling deadline of the i-th product. White spaces can occur freely in input. Input data terminate with an end of file and are guaranteed correct.
输出
For each set of products, the program prints on the standard output the profit of an optimal selling schedule for the set. Each result is printed from the beginning of a separate line.
样例输入
4  50 2  10 1   20 2   30 1
7  20 1   2 1   10 3  100 2   8 2
   5 20  50 10
样例输出
80
185
题意为:商店有N个物品,每个物品有利润,以及一个可以卖出的时间上限。商店每个时间段只能卖一件商品,求可以获得的最大利润。
很容易想到的一个策略:按照时间排好序,然后依次放入,如果某一个放不进去,比较它的value与已放入的最小value,如果前者大于后者,
则使它替换掉那个最小的value这样就可以用优先队列讲此题搞定。而从价值大小考虑,先按照价值从大到小排序,然后依次从起最后时刻放起,
能放进去就放,放不进去就算了这是个贪心的做法,可以证明是对的按照时间从后往前放入,可以用并查集来维护,findroot(i)表示i时刻之前,与其最近的空闲时间.
优先队列:

#include <iostream>#include <cstdio>#include <algorithm>#include <queue>#define N 10010using namespace std;int n;priority_queue <int> q; //声明一个优先队列struct CData{    int Profit;    int Day;    //按照截止时间、利益的顺序排序}p[N];bool cmp(CData a,CData b){    if( a.Day != b.Day )            return a.Day < b.Day;  //时间从小到大排序    return a.Profit > b.Profit;  //利润从大到小排序}void Input(){    for(int i = 0 ; i < n ; i++ )    {        scanf("%d %d",&p[i].Profit,&p[i].Day);    }}void Solve(){    sort(p,p+n,cmp);    int nLeft = 0;  //当前剩余时间段    int nLast = 0;  //当前时间    while(!q.empty()) q.pop();    for(int i = 0 ; i < n ; i++ )    {        nLeft += p[i].Day - nLast;        nLast = p[i].Day;        if( nLeft )        {            //有剩余时间,则往里面添加            nLeft--;            q.push(p[i].Profit);        }else        {            //没有剩余时间,替换掉最小的            if(!q.empty() && q.top() < p[i].Profit)            {                q.pop();                q.push(p[i].Profit);            }        }    }    int ans = 0;    while(!q.empty())    {        //最终队列里的就是答案        ans += q.top();        q.pop();    }    printf("%d\n",ans);}int main(){    while(scanf("%d",&n)==1)    {        Input();        Solve();    }    return 0;}
并查集

#include <iostream>#include <cstdio>#include <algorithm>#define N 10010using namespace std;int n;struct CData{    int Profit;    int Day;    //按照截止时间、利益的顺序排序}p[N];bool cmp(CData a,CData b){    //if( a.Day != b.Day )            //return a.Day < b.Day;  //时间从小到大排序    return a.Profit > b.Profit;  //利润从大到小排序}int father[N];int findroot(int k){    return k==father[k]?k:father[k]=findroot(father[k]);}int main(){    int ans,nlast;    while(scanf("%d",&n)==1)    {        nlast = 0;  //最晚的截止时间        ans = 0;        for(int i = 0 ; i < n ; i++ )        {            scanf("%d%d",&p[i].Profit,&p[i].Day);            nlast = max(nlast,p[i].Day);        }        sort(p,p+n,cmp);    //按照利益排序        for(int i = 0 ; i <= nlast ; i++ )        {            father[i]=i;    //初始化,每一个时间的最近空闲时间都是其本身        }        for(int i = 0 ; i < n ; i++ )        {            int k = findroot( p[ i ].Day ); //找到最近的空闲时间            if( k )            {                //如果存在最近的空闲时间,讲起塞入                ans += p[i].Profit;                father[k] = findroot(k-1);            }        }        printf("%d\n",ans);    }    return 0;}


0 0