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组,结束的位置越靠前,只要划分适当,其第一份的页数就越可能减小。
DP解:这是我的第一个解,可以说用dp解这个题目消耗人的精力远比正解高
意思就是给出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; }
相关文章推荐
- Nginx入门笔记_第一篇
- Linux install iperf3 record
- linux基本使用
- Linux NFS 服务器和客户端
- Linux 10字符串命令病毒的处理记录
- 移除linux内核中dm9000驱动+编译uboot
- Linux基础知识(一)
- Linux 用户线程切换分析
- CentOs虚拟机NAT模式下静态IP的配置
- linux面试之内存管理
- centos上使用第三方yum源获取更多的rpm包
- linux面试之-多线程的用武之地
- 全球排名前500的网站都是做什么的
- linux面试之-谈谈你理解的堆和栈
- Linux系统中的ps进程查看命令使用实例集锦
- 移植opencv2.3.1 到tiny6410
- linux面试基础考题
- CentOS针对磁盘IO[jdb2进程]的优化
- 【Big Data】HADOOP集群的配置(二)
- Android Camera API2.0下全新的Camera FW/HAL架构简述