UVA 714 Copying Books
题意:给定一个序列,不改变每个数的相对位置,将其分成k份,求使这k份里每一份所有元素之和的最大值最小的划分方式。
思路:使最大子序列之和的最大值最小,经典的二分题,与POJ 3273十分相似,不同的是这里在满足分法和使最大值最小的条件下要使前面子序列最小,并以此往后推,我们可以先将最大序列的最小值情况找到,用一个vis数组来记录需要输出/的位置,如果满足情况下输出的/不够k即满足情况的序列可能不止一种情况,只要从后往前找到第一个满足情况的序列再判断所分序列数是否达到k,若不到则从前往后以此补就行。
POJ 3273题解
#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <set>
#include <map>
#include <list>
#include <stack>
#include <deque>
#include <queue>
#include <vector>
#include <algorithm>
#include <functional>
#define debug(x) cout << "--------------> " << x << endl
using namespace std;
const double PI = acos(-1.0);
const double eps = 1e-10;
const long long INF = 0x7fffffff;
const long long MOD = 1000000007;
const int MAXN = 500 + 7;
int p[MAXN];
int main()
{
int T, n , k;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &k);
long long sum = 0;
int Min = 0;
for(int i = 0; i < n; ++i)
{
scanf("%d", &p[i]);
sum += p[i];
Min = max(Min, p[i]);
}
long long l, r, mid;
l = 0;
r = sum;
while(l <= r)
{
mid = (l + r) / 2;
if(mid < Min)
{
l = mid + 1;
continue ;
}
int cnt = 1;
long long temp = 0;
for(int i = 0; i < n; ++i)
{
temp += p[i];
if(temp > mid)
{
cnt++;
temp = p[i];
}
}
if(cnt <= k)
r = mid-1;
else
l = mid+1;
}
long long temp = 0;
int cnt = 0;
int vis[MAXN];
memset(vis, 0, sizeof(vis));
for(int i = n-1; i >= 0; --i)
{
temp += p[i];
if(temp > l)
{
cnt++;
temp = p[i];
vis[i] = 1;
}
}
if(cnt < k-1)
{
for(int i = 0; i < n; ++i)
{
if(!vis[i])
{
vis[i] = 1;
cnt++;
if(cnt == k-1)
break;
}
}
}
for(int i = 0; i < n; ++i)
{
if(!i)
printf("%d",p[i]);
else
printf(" %d", p[i]);
if(vis[i])
printf(" /");
}
printf("\n");
}
}