??xml version="1.0" encoding="utf-8" standalone="yes"?> PKU 1018 Communication System 法分类Q?/span> 枚D+贪心 原文Q?/span> Communication System Time
Limit:1000MS Memory Limit:10000K Description We have received an order from
Pizoor Communications Inc. for a special communication system. The system
consists of several devices. For each device, we are free to choose from
several manufacturers. Same devices from two manufacturers differ in their
maximum bandwidths and prices. Input The first line of the input file
contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by
the input data for each test case. Each test case starts with a line containing
a single integer n (1 ≤ n ≤ 100), the number of devices in the communication
system, followed by n lines in the following format: the i-th line (1 ≤ i ≤ n)
starts with mi (1 ≤ mi ≤ 100), the number of manufacturers for the i-th device,
followed by mi pairs of positive integers in the same line, each indicating the
bandwidth and the price of the device respectively, corresponding to a
manufacturer. Output Your program should produce a single
line for each test case containing a single number which is the maximum
possible B/P for the test case. Round the numbers in the output to 3 digits
after decimal point. Sample Input 1
3 3
100 25 150 35 80 25 2
120 80 155 40 2
100 100 120 110 Sample Output 0.649 Source Tehran 2002, First Iran Nationwide
Internet Programming Contest 中文描述Q?/span> 你需要购?/span>nU设备来l一个通信pȝQ每一U设备,又是׃些不同的刉商生的,不同刉商生的同U设备会有不同的带宽和h根{现在你要在每一个设备的刉商中选一个,使得购买?/span>nU设备,它们带宽的最gh之和的比最大?/span> 题目分析Q?/span> 一开始想到的是暴搜Q但是搜索的深度辑ֈ100Q时间肯定是不允许的。想要解册题,必须扑ֈ一个好的查扄略。再x看这题的特点Q最后的{案Q带宽是选取所有设备中的最|而h格是选取所有设备的hd。如果某个制造商生的某U设备,它的带宽较高而hD低,那么选取它的可能性就比较大。再q一步说Q如果所选取?/span>nU设备的带宽的最?/span>b已经定Q那么对于某U设备,我们可以在那些生q种讑֤的,带宽大于{于b的制造商中进行选择。当然是选那个h格最低的讑֤Q因为答案的分子已经定?/span>bQ所以分母越越好。看来只要枚?/span>bQ再对于每个讑֤贪心的选择最h格就可以了,旉复杂度ؓOQ?/span>mnBQ,B为带宽枚丄数量。但问题又来了,应该怎么枚D带宽Q题目中q未l出带宽的取D_如果?/span>0..maxB一个一个枚丄话,既费时又会造成q多重复情况Q如果枚N些在输入中出现的两个q箋带宽之间的|最后的{案是一LQ。所以我们应该采取某个方法记录输入中出现q的带宽Q?/span>STL中的set是个不错的选择Q,再枚举这些带宽。在枚D中,可能出现q种情况Q枚?/span>bQ选择?/span>nU设备,但选择的所有设备的带宽都大?/span>bQ那么最l用b/price׃是这U情늚正确{案。其实不用担心,因ؓ正确{案一定大?/span>b/price。假设上面这U情늚实际带宽最值是b’Q那个当我们再去枚Db’Ӟ臛_有一个设备的带宽{于b’Q这ơ得到的{案也就是上面U情늚{案Q所以最l还是能得到正确解?/span> 代码Q?/span> #include
<iostream> #include
<map> #include
<set> #include
<climits> using
namespace std; const int
MAX = 105; struct Info { int band, price; }; struct
Device { int manuNum; Info info[MAX]; map<int, int> minPrice; //map[i] = j 表示带宽>=i的最h格是j int minBand, maxBand; }; Device
device[MAX]; int
deviceNum; set<int>
band; //输入中出现过?/span>band set<int>::iterator
start, end; int
maxBand, minBand; //需要枚丄band的最?/span> int cmp(
const void *a , const void *b ) { Info *c = (Info *)a; Info *d = (Info *)b; if(c->band != d->band) return
d->band - c->band; else return
c->price - d->price; } void Input
() { int i, j, max, min; band.clear(); cin>>deviceNum; for (i=0; i<deviceNum; i++) { device[i].minBand
= INT_MAX; device[i].maxBand
= -1; cin>>device[i].manuNum; for (j=0;
j<device[i].manuNum; j++) { cin>>device[i].info[j].band>>device[i].info[j].price; band.insert(device[i].info[j].band); if
( device[i].info[j].band > device[i].maxBand ) device[i].maxBand
= device[i].info[j].band; if
( device[i].info[j].band < device[i].minBand ) device[i].minBand
= device[i].info[j].band; } } } void Pre () //预处?/span> { int i, j, min, b; //计算所需枚D的带宽的最?/span> maxBand = INT_MAX; //maxBand为所有设备带宽最大值的最?/span> minBand = INT_MAX; //minBand为所有设备带宽最值的最?/span> for (i=0; i<deviceNum; i++) { if (
device[i].maxBand < maxBand ) maxBand
= device[i].maxBand; if (
device[i].minBand < minBand ) minBand
= device[i].minBand; } //对于每个讑֤Q找到带宽大于等于某一值的最h?/span> for (i=0; i<deviceNum; i++) { //band从大到小Q?/span>band相等?/span>price从小到大 qsort(device[i].info,
device[i].manuNum, sizeof(Info), cmp); device[i].minPrice.clear(); min =
device[i].info[0].price; b =
device[i].info[0].band; device[i].minPrice[b]
= min; for (j=1;
j<device[i].manuNum; j++) { if
( device[i].info[j].band == b ) continue; if
( device[i].info[j].price < min ) { min
= device[i].info[j].price; } b
= device[i].info[j].band; device[i].minPrice[b]
= min; } } } void Solve
() { Pre(); int b, i, totalPrice; double rate, ans; map<int, int>::iterator
it; ans = 0; start = band.find(minBand); end = band.find(maxBand); end ++; while ( start != end ) { b = *start; start ++; totalPrice = 0; for (i=0;
i<deviceNum; i++) { //扑ֈ带宽大于{于b的最h?/span> for
(it=device[i].minPrice.begin(); it!=device[i].minPrice.end(); it++) { if
( it->first >= b ) { totalPrice
+= it->second; break; } } } rate = double(b)
/ totalPrice; if ( rate >
ans ) ans
= rate; } printf("%.3f\n", ans); } int main () { int test; cin>>test; while ( test -- ) { Input (); Solve (); } return 0; }
思\Q?br> 告诉你[a,b]之间1个数的奇偶情况,那么你就可以在a-1和b之间q一条边Q权值就是其奇偶情况。这样一来,比如[1,2]和[3,4]的情况已知,[1,4]的情况也q道了。当题目l出[a,b]的情冉|Q首先分别从a和b往上找Q找C们的根r1和r2Q如果r1 != r2Q表Ca,b之间的奇偶情况还不确定,将r1和r2之间qv来,Ҏa到r1的权倹{b到r2的权值和题目所l的奇偶情况Q设|r1和r2之间的权|以符合题目要求。若r1 == r2Q则表示[a,b]之间情况已确定,Ҏa到r1的权值和b到r2的权|可以判断题目所l的[a,b]的情冉|否ؓ真?br> 其实当时做的时候,q不是很懂,但没惛_E里糊涂的AC了。推荐一下这个网:http://hi.baidu.com/fandywang_jlu/blog/item/b49e40893ddbb0b00f244485.htmlQ这里面介绍q查集挺详细的,q有不少推荐题目Q有些还不会做。:P
代码Q?br>
#include <iostream>
#include <map>
using namespace std;
const int MAX = 10005;
int n, p;
int pre[MAX];
int parity[MAX]; //i到目前集合的根的奇偶情况
map<int, int> numIndex; //用于L?br>
int Find (int x)
{
if ( pre[x] == -1 )
return x;
int f;
f = Find(pre[x]);
parity[x] = (parity[x] + parity[pre[x]]) % 2; //此时pre[x]已指向最l的集合的根
pre[x] = f;
return f;
}
bool Query (int x, int y, int odd)
{
int r1, r2;
r1 = Find(x);
r2 = Find(y);
if ( r1 == r2 )
{
if ( (parity[x] + parity[y]) % 2 == odd )
return true;
else
return false;
}
else //只是r1接到r2下面Q这边还可以优化
{
pre[r1] = r2;
parity[r1] = (parity[x] + parity[y] + odd) % 2;
return true;
}
}
void Solve ()
{
int i, x, y, index, idx1, idx2, odd;
char s[10];
scanf("%d%d", &n, &p);
index = 0;
for (i=0; i<p; i++)
{
scanf("%d%d%s", &x, &y, &s);
x --;
if ( numIndex.find(x) == numIndex.end() )
numIndex[x] = index ++;
idx1 = numIndex[x];
if ( numIndex.find(y) == numIndex.end() )
numIndex[y] = index ++;
idx2 = numIndex[y];
if ( strcmp(s, "odd") == 0 )
odd = 1;
else
odd = 0;
if ( Query(idx1, idx2, odd) == false )
{
break;
}
}
printf("%d\n", i);
}
void Init ()
{
memset(pre, -1, sizeof(pre));
}
int main ()
{
Init();
Solve();
return 0;
}
]]>
]]>
]]>
]]>
By overall bandwidth (B) we mean the minimum of the bandwidths of the chosen
devices in the communication system and the total price (P) is the sum of the
prices of all chosen devices. Our goal is to choose a manufacturer for each
device to maximize B/P.
]]>
]]>
题目来源Q?/span>
PKU 1505 Copying Books
法分类Q?/span>
DP
原文Q?/span>
Copying Books
Time Limit: 3000MS |
Memory Limit: 10000K |
|
Total Submissions: 1806 |
Accepted: 404 |
Description
Before the
invention of book-printing, it was very hard to make a copy of a book. All the
contents had to be re-written by hand by so called scribers. The scriber had
been given a book and after several months he finished its copy. One of the
most famous scribers lived in the 15th century and his name was Xaverius
Endricus Remius Ontius Xendrianus (Xerox). Anyway, the work was very annoying
and boring. And the only way to speed it up was to hire more scribers.
Once upon a time, there was a theater ensemble that wanted to play famous
Antique Tragedies. The scripts of these plays were divided into many books and
actors needed more copies of them, of course. So they hired many scribers to
make copies of these books. Imagine you have m books (numbered 1, 2 ... m) that
may have different number of pages (p1, p2 ... pm) and you want to make one
copy of each of them. Your task is to divide these books among k scribes, k
<= m. Each book can be assigned to a single scriber only, and every scriber
must get a continuous sequence of books. That means, there exists an increasing
succession of numbers 0 = b0 < b1 < b2, ... < bk-1 <= bk
= m such that i-th scriber gets a sequence of books with numbers between bi-1+1
and bi. The time needed to make a copy of all the books is determined by the
scriber who was assigned the most work. Therefore, our goal is to minimize the
maximum number of pages assigned to a single scriber. Your task is to find the
optimal assignment.
Input
The input
consists of N cases. The first line of the input contains only positive integer
N. Then follow the cases. Each case consists of exactly two lines. At the first
line, there are two integers m and k, 1 <= k <= m <= 500. At the
second line, there are integers p1, p2, ... pm separated by spaces. All these
values are positive and less than 10000000.
Output
For each
case, print exactly one line. The line must contain the input succession p1,
p2, ... pm divided into exactly k parts such that the maximum sum of a single
part should be as small as possible. Use the slash character ('/') to separate
the parts. There must be exactly one space character between any two successive
numbers and between the number and the slash.
If there is more than one solution, print the one that minimizes the work
assigned to the first scriber, then to the second scriber etc. But each scriber
must be assigned at least one book.
Sample Input
2
9
3
100
200 300 400 500 600 700 800 900
5
4
100
100 100 100 100
Sample Output
100
200 300 400 500 / 600 700 / 800 900
100
/ 100 / 100 / 100 100
Source
中文描述Q?/span>
题目大意是给?/span>mQ?/span>1…mQ本书,每本书有Pm,?/span>kQ?/span>k<=mQ个员工来复印这些书。每本书只能分配l一个员工来复印Qƈ且每个员工必dCD连l的书籍Q每个员工复印的旉取决于所复印书籍的总页数。让你给出相应的分配Q得分配给员工的书c页数的最大值尽量小。注意,如果有多U分配的ҎQ得第一个员工的书籍|量,其次是第二个、第三个……以此cL?/span>
题目分析Q?/span>
我们可以从后往前推Q最后一个员工,也就是第k个员工,他至要复印W?/span>m本书Q至多可以复印第k本到W?/span>m本(因ؓ臛_要分配给?/span>k-1个员工每Z本书Q。假设,W?/span>k名员工复制第iQ?/span>k<=i<=mQ本书到W?/span>m本书Q那么,所有员工复Cc的最时间就为第k名员工所需的时间以及前k-1名员工复制前i-1本书所需最时间的较大的那个时间。这P问题的规模就?/span>k个员工复?/span>m本书减小Ck-1个员工复?/span>i-1本书Q而且求解q程中会不断遇到?/span>a个员工复印前b本书的最时间。ؓ了减问题的规模以及记录重复子问题的解,可以用DP?/span>
但仅仅算出最时间的不够的,q要l出分配的方案,q个E微有点J琐。因为题目中_如果有多U最优的分配ҎQ应该让前面的员工分配的|量。那么,可以从后推,在当前的员工所复印的书c页数没有超q最大页数的情况下,让其复印的页数最大化。如果超q了最大页敎ͼ把q本书分配给前一名员工。最后再按顺序将分配l果输出出来?/span>
代码Q?/span>
#include
<cstdio>
#include
<climits>
#include
<cstring>
const int
MAX = 505;
int
book[MAX];
__int64
total[MAX]; //1~n本书的页?/span>
int k, m;
__int64
f[MAX][MAX]; //f[i][j] = k 表示?/span>i个h复制?/span>j本书所需最时间是k
__int64
max;
void Input
()
{
scanf("%d%d", &m,
&k);
int i;
for (i=1; i<=m; i++)
scanf("%d",
&book[i]);
}
__int64 Sum
(int s, int e) //W?/span>s本书到第e本书的总页?/span>
{
return (total[e] - total[s-1]);
}
__int64 Max
(__int64 a, __int64 b)
{
return ( a>b?a:b );
}
__int64 Min
(int x, int y) //?/span>x个h复制?/span>y本书所需的最时?/span> x<=y
{
//考虑Ҏ情况
if ( f[x][y] != -1 )
return f[x][y];
if ( y == 0 )
return ( f[x][y]
= 0 );
if ( x == 0 )
return ( f[x][y]
= INT_MAX );
int i;
__int64 temp;
f[x][y] = INT_MAX;
for (i=x-1; i<y; i++)
{
//W?/span>x个h复制W?/span>i+1到第y本书与前x-1个h复制?/span>i本书的时间较大的旉
temp = Max(
Min(x-1, i), Sum(i+1, y) );
if ( temp <
f[x][y] )
{
f[x][y]
= temp;
}
}
return f[x][y];
}
void Output
()
{
int i, p;
__int64 temp;
int slash[MAX];
max = f[k][m];
memset(slash, 0, sizeof(slash));
temp = 0;
p = k;
for (i=m; i>0; i--)
{
//让后面的员工量复印最多的书籍
if ( temp +
book[i] > max || i < p )
{
slash[i]
= 1;
temp
= book[i];
p
--;
}
else
{
temp
+= book[i];
}
}
for (i=1; i<=m; i++)
{
printf("%d",
book[i]);
if ( slash[i] ==
1 )
printf("
/ ");
else if ( i != m
)
printf("
");
}
printf("\n");
}
void Solve
()
{
int i, j;
//预处理书c页数的?/span>
total[0] = 0;
for (i=1; i<=m; i++)
total[i] =
total[i-1] + book[i];
memset(f, -1, sizeof(f));
Min(k, m);
Output();
}
int main ()
{
int test;
scanf("%d",
&test);
while ( test-- )
{
Input ();
Solve ();
}
return 0;
}
E序分析与心得:
旉复杂?/span>O(n2)Q空间复杂度O(n2)?/span>
在用记忆化搜索解?/span>DP问题Ӟ往往比较W合人的思维Q容易想到模型,~程比较单。在解题q程中,除了可以按照常理着推,也可以尝试逆向思维Q从最后的状态倒着推,q样可以佉K题想得更加透彻Q有比较好的效果?/span>
Source Code
Problem: 2907 | User: QuXiao | |
Memory: 176K | Time: 0MS | |
Language: C++ | Result: Accepted |
#include <iostream>
#include <climits>
using namespace std;
struct Point
{
int x, y;
};
int num, X, Y;
Point start, beeper[15];
int shortest;
int visited[15];
int Length (Point p1, Point p2)
{
return abs(p1.x - p2.x) + abs(p1.y - p2.y);
}
void Input ()
{
int i;
cin>>X>>Y;
cin>>start.x>>start.y;
cin>>num;
for (i=0; i<num; i++)
cin>>beeper[i].x>>beeper[i].y;
}
void DFS (int cur, int len, int n)
{
if ( n == num )
{
int t = Length(beeper[cur], start);
if ( len + t < shortest )
shortest = len + t;
}
else if ( len < shortest )
{
int i;
for (i=0; i<num; i++)
{
if ( visited[i] == 0 )
{
visited[i] = 1;
DFS (i, len+Length(beeper[cur], beeper[i]), n+1);
visited[i] = 0;
}
}
}
}
void Solve ()
{
int i, t;
shortest = INT_MAX;
memset(visited, 0, sizeof(visited));
for (i=0; i<num; i++)
{
t = Length(beeper[i], start);
visited[i] = 1;
DFS (i, t, 1);
visited[i] = 0;
}
cout<<"The shortest path has length "<<shortest<<endl;
}
int main ()
{
int test;
cin>>test;
while ( test-- )
{
Input ();
Solve ();
}
return 0;
}