oyjpArt ACM/ICPC算法程序设计空间

// I am new in programming, welcome to my blog
I am oyjpart(alpc12, 四城)
posts - 224, comments - 694, trackbacks - 0, articles - 6

Supermarket
Time Limit:2000MS  Memory Limit:65536K
Total Submit:987 Accepted:369

Description
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.

 

 

 

 

 

Input
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.

Output
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.

Sample Input

4  50 2  10 1   20 2   30 1
7  20 1   2 1   10 3  100 2   8 2
5 20  50 10

 

 

 

 

 

Sample Output

80
185

 

 

 

 

 

Hint
The sample input contains two product sets. The first set encodes the products from table 1. The second set is for 7 products. The profit of an optimal schedule for these products is 185.

Source
Southeastern Europe 2003


贪心是很容易想到的 尽可能将利润大的选中 然后尽可能放到DeadLine附近去

这里就有一个问题 怎样尽快拿到一个deadLine往上数第一个空闲日期?

想法0:直接搜o(n^2)的查找总费时 可能超时!

想法1:线段树!问题建模:一个数的前面第几个是空闲?
对10000的区间建立线段树 可以将查询复杂度降低到NlogN

想法2:   并查集!对一个连续非空闲区间建立集合 其代表元选择上面第一个空闲的日期!时间复杂度再降低到o(N)! 系数为ackman函数 很小

Problem Id:1456  User Id:oyjpart
Memory:172K  Time:15MS
Language:G++  Result:Accepted

//by oyjpArt
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 10010;
int ng;
struct good{int p, d;}g[N];
int p[N];
bool flag[N];

bool operator < (const good& a, const good& b) {return a.p > b.p; }

int find_set(int a) {
 int t = a;
 while(t != p[t]) t = p[t];
 int r = a;
 while(r != p[r]) { int k = p[r]; p[r] = t; r = k; }
 return t;
}

int main() {
 int i;
 while(scanf("%d", &ng) != EOF) {
  memset(flag, 0, sizeof(flag));
  int maxd = 0;
  for(i = 0; i<ng; i++) {
   scanf("%d%d", &(g[i].p), &(g[i].d));
   if(g[i].d > maxd) maxd = g[i].d;
  }
  for(i = 1; i<maxd; i++)
   p[i] = i;
  sort(g, g+ng);
  int cnt = 0;
  for(i = 0; i<ng; i++) {
   int d;
   if(!flag[g[i].d]) { d = g[i].d; p[d] = d-1; }
   else d = find_set(g[i].d);
   if(d > 0) cnt += g[i].p;
   flag[d] = 1;
   if(d > 0 && flag[d+1]) p[d] = p[d-1];
  }
  printf("%d\n", cnt);
 }
 return 0;
}

Feedback

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-11 18:39 by nick
if(flag[d+1]) p[d] = p[d-1];

加這一行的目的在哪呢?

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-11 20:07 by oyjpart
p[d]代表选中在D之前第一个空闲的日子
就是说d这个日期用了之后 那么第一个空闲的日子就一定时p[d-1];
也可以这样写:
for(i = 0; i<ng; i++) {
int d = g[i].d;
if(flag[g[i].d]) d = find_set(g[i].d);
if(d > 0) cnt += g[i].p;
flag[d] = 1;
p[d] = p[d-1]; //如果p[d-1]没用用过 那么p[d-1]就应该=d-1
}

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-12 03:00 by nick
@oyjpart

可以舉個例子嗎? 真的有些難懂
什麼情況會需要用到這個
抱歉, 謝謝你的回覆

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-12 17:20 by oyjpart
抱歉 我说的不是很清楚

首先 我这里的p[]数组代表的时parent 也就是在树形的并查集中父结点的标号 比如 如果1节点(根)的子节点是2,3 则p[2] = p[3] = 1; 而根节点的父结点将会被初始化为自己的标号 如p[1] = 1;
在树形的并查集中 我们将根节点定义为这个集合的代表元
而在这个题目中我们将代表元定义为这个从某一天往上数第一个空闲的日期 比如 日期
1 2 3 4 5 6 7 8 9 10
如果3是空闲(没有被分配一个商品)而4,5都已经分配 那么p[4]=p[5] = 3;
如果4也是空闲 那么p[3] = 3; p[4] = 4;
这样 我们在分配商品的时候 要得到从某一天往上数第一个空闲的日期 就是直接查找这一天所在集合里面的代表元就可以了
从另外一个角度上来看 这个集合其实时一个空闲的天 后面跟着一连串的非空闲天
比如
1 2 3 4 5 6 7 8 9 10
如果3是空闲(没有被分配一个商品)而4,5都已经分配 6没有分配 那这个集合就是3,4,5 其中代表元为3
以下面这段代码来作说明吧
for(i = 0; i<ng; i++) {
int d = g[i].d;
if(flag[g[i].d]) d = find_set(g[i].d);
if(d > 0) cnt += g[i].p;
flag[d] = 1;
p[d] = p[d-1]; //如果p[d-1]没用用过 那么p[d-1]就应该=d-1
}
如果flag[g[i].d] is true 代表g[i].d这一天已经被分配 于是空闲的天就是find_set(g[i].d);
否则 d = g[i].d; 就是这一天 这个时候集合只有一个元素 就是自己作为代表元
if(d > 0) cnt += g[i].p; 因为有可能代表元为0(设置数据的时候p[d-1]为了不越界 故意留出0) 实际上代表没有空闲 比如1,2,3,4都满了 你要放在3的地方 就会得到0 这个位置 所以要>0才能cnt += g[i].p;
flag[d] = 1; 接着把刚才找到的空闲处标记
p[d] = p[d-1]; 然后把刚才找到的空闲处的代表元设置一下
这里的设置对于下面2中情况都是对的
情况1: 1 2 3 4 5
2空闲 3空闲 我在3处放 然后标记p[3] = p[2] = 2;
情况2: 1 2 3 4 5 6
2满 3满 4空闲 我在4处放 然后标记 p[4] = p[3] = 1
基本上就是这样了 没有解释清楚 真不好意思 :)

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-18 21:51 by nick
呵呵, 真的很謝謝你回覆這麼說
我懂
for(i = 0; i<ng; i++) {
int d = g[i].d;
if(flag[g[i].d]) d = find_set(g[i].d);
if(d > 0) cnt += g[i].p;
flag[d] = 1;
p[d] = p[d-1]; //如果p[d-1]没用用过 那么p[d-1]就应该=d-1
}

但就是不懂原本程序的 if(flag[d+1]) p[d] = p[d-1]; 這一行的意義在哪

如果這個程序不加這一行是一定會錯

但什麼樣的 case 會導致錯誤呢?

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-18 21:56 by nick
喔喔, 我想到了.. 呵呵, 謝謝你

原本那樣寫讓我比較不好理解

不過後來的寫法
p[d] = p[d-1]; //如果p[d-1]没用用过 那么p[d-1]就应该=d-1

這邊要 if(d>0) 不然 p[d-1] 會 runtime error ^^

# re: PKU 1456 Supermarket 并查集加速实例  回复  更多评论   

2007-03-19 17:37 by oyjpart
o 对 呵呵~~ 我改一下
:)

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理