线段树- PKU Moving Tables

 

Moving Tables
Time Limit: 1000MS
 
Memory Limit: 10000K
Total Submissions: 3262
 
Accepted: 1171
Description
The famous ACM (Advanced Computer Maker) Company has rented a floor of a building whose shape is in the following figure.

The floor has 200 rooms each on the north side and south side along the corridor. Recently the Company made a plan to reform its system. The reform includes moving a lot of tables between rooms. Because the corridor is narrow and all the tables are big, only one table can pass through the corridor. Some plan is needed to make the moving efficient. The manager figured out the following plan: Moving a table from a room to another room can be done within 10 minutes. When moving a table from room i to room j, the part of the corridor between the front of room i and the front of room j is used. So, during each 10 minutes, several moving between two rooms not sharing the same part of the corridor will be done simultaneously. To make it clear the manager illustrated the possible cases and impossible cases of simultaneous moving.

For each room, at most one table will be either moved in or moved out. Now, the manager seeks out a method to minimize the time to move all the tables. Your job is to write a program to solve the manager's problem.
Input
The input consists of T test cases. The number of test cases ) (T is given in the first line of the input file. Each test case begins with a line containing an integer N , 1 <= N <= 200, that represents the number of tables to move.
Each of the following N lines contains two positive integers s and t, representing that a table is to move from room number s to room number t each room number appears at most once in the N lines). From the 3 + N -rd
line, the remaining test cases are listed in the same manner as above.
Output
The output should contain the minimum time in minutes to complete the moving, one per line.
Sample Input
3
4
10 20
30 40
50 60
70 80
2
1 3
2 200
3
10 100
20 80
30 50
Sample Output
10
20
30
Source
Taejon 2001
第一种解答方案
该问题可以等效为求一个区间上相交线段之间的并。可以采用线段树,更确切的讲,应该是点树。点树的节点表示房间前的走廊空间,叶子节点代表其中上下相对的两个房间前的一个走廊空间,共有200个这样的走廊空间,其中房间i和房间i+1对应一个走廊空间。中间节点(左右端点分别为走廊空间i和走廊空间j)的左右儿子节点分别为(i,m),(m+1,j),其中m=(i+j)/2。第一步是建立线段树,采用静态数组结构,数组容量为200*2。建立时候注意左儿子的左右端分别为left,m,右儿子的左右端分别为m+1,right,其中m = (left+right)/2。第二步是查找输入的区间,从点树的根节点开始查找,直到某个节点的左
右端与输入区间重合,并进行累加;否则根据节点的左右端与输入区间的关系来选择左右子树往下查找。假设节点的左右端分别为a,b,节点对应区间的中点为m,m=(a+b)/2,输入区间的左右边界分别为left,right。选择左右子树的逻辑如下:
if(left>m)
 // 选择右子树
else if(right<=m)
// 选择左子树
else
 // 左右子树同时选择
最后一步是进行统计。统计的递归方法是先对左右子树分别递归求解移动次数,并取最大值,然后将该最大值累加与根节点对应区间的重复输入区间数目(segnum),即所求解。
Code:
#include <iostream>
using namespace std;
#define MAXN 200
#define TIME 10
typedef struct
{
 int segnum;
 int left,right;
 int lchild,rchild;
}treeNode;
static treeNode tree[MAXN*2]; //twice of MAXN,since creating segment tree requires memory junks twice the number of input elements
static int ss[MAXN];
static int tt[MAXN];
static int tlen;
static int tree_create(int ,int );
static void insert(int ,int ,int );
static int getMinMove(int );
inline int maxFunc(int ,int );
inline int minFunc(int ,int );
inline int maxFunc3(int ,int ,int);
inline int minFunc3(int ,int ,int);
int main(int argc,char **argv)
{
 int T,N;
 int xmax,xmin;
 int i,j;
 int left,right;
 int root;
 int result;
 cin>>T;
 for(i=0;i<T;i++)
 {
  cin>>N;
  xmax = 0;
  xmin = MAXN+1;
  for(j = 0;j<N;j++)
  {
   cin>>ss[j]>>tt[j];
   xmax = maxFunc3(xmax,(ss[j]>>1)+ss[j]%2,(tt[j]>>1)+tt[j]%2);
   xmin = minFunc3(xmin,(ss[j]>>1)+ss[j]%2,(tt[j]>>1)+tt[j]%2);
  }
  tlen = -1;
  root = tree_create(xmin,xmax);
  for(j = 0;j<N;j++)
  {
   right = maxFunc(ss[j],tt[j]);
   left = minFunc(ss[j],tt[j]);
   insert(root,(left>>1)+left%2,(right>>1)+right%2);
  }
  result = getMinMove(root)*TIME;
  cout<<result<<endl;
 }
 return 0;
}
static int tree_create(int left,int right)
{
 int cur = ++tlen;
 tree[cur].left = left;
 tree[cur].right = right;
 tree[cur].segnum = 0;
 if(left==right)
 {
  tree[cur].lchild = tree[cur].rchild = -1;
 }
 else
 {
  int m = (left+right)/2;
  tree[cur].lchild = tree_create(left,m);
  tree[cur].rchild = tree_create(m+1,right);
 }
 return cur;
}
static void insert(int cnode,int left,int right)
{
 if(cnode==-1||left>right)
  return;
 if(tree[cnode].left==left&&tree[cnode].right==right)
 {
  tree[cnode].segnum++;
 }
 else
 {
  int m = (tree[cn  if(left>m)
  {
   insert(tree[cnode].rchild,left,right);
  }
  else if(right<=m)
  {
   insert(tree[cnode].lchild,left,right);
  }
  else
  {
   insert(tree[cnode].lchild,left,m);
   insert(tree[cnode].rchild,m+1,right);
  }ode].left+tree[cnode].right)/2;
 }
}
static int getMinMove(int cnode)
{
 if(cnode==-1)
  return 0;
 int maxValue =0;
 int lmax = getMinMove(tree[cnode].lchild);
 int rmax = getMinMove(tree[cnode].rchild);
 maxValue = maxFunc(maxValue,lmax);
 maxValue = maxFunc(maxValue,rmax);
 return maxValue+tree[cnode].segnum;
}

inline int maxFunc(int a,int b)
{
 return a>b?a:b;
}
inline int maxFunc3(int a,int b,int c)
{
 return maxFunc(maxFunc(a,b),c);
}
inline int minFunc(int a,int b)
{
 return a<b?a:b;
}
inline int minFunc3(int a,int b,int c)
{
 return minFunc(minFunc(a,b),c);
}
 
基于点树的思想,具体实现时可以不采用点树的形式。数组count[200+1]用于统计经过走廊空间的移动线路的个数。每输入一个移动线路,则将移动线路经过的走廊空间的移动线路个数加一,并同时与当前记录的最大值进行比较。如果大于最大值,则更新最大值。最后程序输出该最大值,即所求解。
Code:
#include <iostream>
using namespace std;
#define MAXN 200
#define TIME 10
static int count[MAXN+1];
static int ss[MAXN];
static int tt[MAXN];
inline int maxFunc(int ,int );
inline int minFunc(int ,int );
int main(int argc,char **argv)
{
 int T,N;
 int i,j,k;
 int left,right;
 int result;
 cin>>T;
 for(i=0;i<T;i++)
 {
  cin>>N;
  result = 0;
  memset(count,0,sizeof(count));
  for(j = 0;j<N;j++)
  {
   cin>>ss[j]>>tt[j];
   left = minFunc((ss[j]>>1)+ss[j]%2,(tt[j]>>1)+tt[j]%2);
   right = maxFunc((ss[j]>>1)+ss[j]%2,(tt[j]>>1)+tt[j]%2);
   
   for(k=left;k<=right;k++)
   {
    count[k]++;
    result = maxFunc(result,count[k]);
   }
  }
  cout<<result*TIME<<endl;
 }
 return 0;
}
inline int maxFunc(int a,int b)
{
 return a>b?a:b;
}
inline int minFunc(int a,int b)
{
 return a<b?a:b;
}
PKU上该题目的讨论上还讲到可以使用DP来解决,正在思考如何使用DP来建立递归方程,如果哪位是用DP来解决这个问题的,请献上你的思路,大家分享一下。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值