您的位置:首页 > 运维架构

UVA - 714 Copying Books 抄书 贪心+二分

2016-03-03 12:49 477 查看
题意:

意思就是给出n本书然后要分成k份,每份总页数的最大值要最小。问你分配方案,如果最小值相同情况下有多种分配方案,输出第一份页数最小的,如果仍有多解,输出第二份最小的,依此类推。

正解:贪心+二分

令存放数据元素的数组是a,因为1<=a[i]&&a[i]<=1e7,所以最大页数的最小值就在这个范围。很显然,可以用二分+贪心来找出这个值(最优值)。

在区间[1,max{a[i]}]进行二分。

对于n个元素组成序列,要划分成k份,使每份页数不超过x,

那么可以从左往右扫描,划分份,让每一份的页数尽量大,尽量使 用的份数少,(贪心)

如果用的最小份数<=x,那么答案在区间[1,x]。(这个是贪心的原因)

否则答案在区间[x+1,max{a[i]}]。

现在找到了最优值,要知道那些元素后面需要加上" /",知道了最优解,就方便了。

找的方法(贪心):对于[pos][ind](即第ind组,在pos位置结束,包含pos位置)

我们需要找到第ind-1组结束的位置,

那么在满足每份页数不超过最优值的情况下(注意这里不需要每份都是最优的,只需要满足不超过最优值)

,要将ind-1组结束的位置尽量往左。

这也是贪心,因为如果同样是ind-1组,结束的位置越靠前,只要划分适当,其第一份的页数就越可能减小。

/**==========================================
*   This is a solution for ACM/ICPC problem
*
*   @source:UVA - 714 Copying Books 抄书
*   @type:  贪心+二分
*   @author: wust_ysk
*   @blog:  http://blog.csdn.net/yskyskyer123 *   @email: 2530094312@qq.com
*===========================================*/

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>

using namespace std;
typedef long long ll;
#define MID   ll  mid=(le+ri)>>1
const int maxn= 500   ;

int a[maxn+5];
ll sum[maxn+5];
bool ok[maxn+5];
ll ans;
int T,n,k;

bool work(ll tans)
{
int cnt=1;
int pos=1;
ll ret=0;
while(pos<=n)
{
ret+=a[pos++];
if(ret>tans)  {ret=a[pos-1];cnt++;}
if(ret>tans)   return false;
}
return cnt<=k;

}
ll bs(ll le,ll ri)
{
while(le<=ri)
{
MID;
if(work(mid)) ri=mid-1;
else  le=mid+1;
}
return le;

}

void find(int pos,int ind)
{
if(ind<=1)  return;
int last=pos-1;//
for( int j=pos-1;j>=ind-1&& sum[pos]-sum[j]<=ans  ;j--)
{
last=j;
}
ok[last]=1;
find(last,ind-1);

}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1] + a[i];
}

ans=bs(1,sum
);//其实把二分范围改为[1,max{a[i]} ]更好;
//        cout<<ans<<endl;
memset(ok,0,(n+1)*sizeof ok[0]);
find(n,k);//递归找到那些元素后面需要加" /",也可以用循环找

for(int i=1;i<=n;i++)
{
if(i>1) putchar(' ');
printf("%d",a[i]);
if(ok[i])
printf(" /");
}
putchar('\n');

}

return 0;
}


DP解:这是我的第一个解,可以说用dp解这个题目消耗人的精力远比正解高

/**==========================================
*   This is a solution for ACM/ICPC problem
*
*   @source: UVA - 714 Copying Books 抄书
*   @type: 贪心+dp+剪枝 这是个麻烦的方法,不好
*   @author: wust_ysk
*   @blog:  http://blog.csdn.net/yskyskyer123 *   @email: 2530094312@qq.com
*===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll INF =1e15;
const int maxn= 500   ;

ll dp[maxn+5][maxn+5];
int a[maxn+5];
int best[maxn+5][maxn+5];
ll sum[maxn+5],ans;
bool ok[maxn+5];
int T,n,k;
void find(int pos,int ind)
{
if(ind<=1)  return;
int last=best[pos][ind];
for(int t=best[pos][ind]-1;t>=ind-1&&dp[t][ind-1]<=ans&&sum[pos]-sum[t]<=ans;t--)
{
last=t;
}
ok[last]=1;
find(last,ind-1);

}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][1]=sum[i]=sum[i-1]+a[i];

}

for(int i=1;i<=n;i++)
{
for(int num=2;num<=k&&num<=i;num++)
{
dp[i][num]=INF;
for(int j=i-1;j>=num-1;j--)
{
dp[i][num]=min( dp[i][num] , max(dp[j][num-1],sum[i]-sum[j])  );
best[i][num]=j;
if(j-1>=1&& max(dp[j-1][num-1],sum[i]-sum[j-1])>max(dp[j][num-1],sum[i]-sum[j])  ) break;
}
}
}

//       printf("%lld\n",dp
[k]);

ans=dp
[k];
memset(ok,0,(n+1)* sizeof ok[0]);

find(n,k);

for(int i=1;i<=n;i++)
{
if(i>1) putchar(' ');
printf("%d",a[i]);
if(ok[i])
printf(" /");
}
putchar('\n');

}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: