题意:

给定n个块,编号从1到n,以及m个操作,初始时n个块是白色。

操作有2种形式:

1 ai xi : 从[1,ai]选xi个块,将这些块涂白。

2 ai xi:从[ai,n]选xi个块,将这些块涂白。

可以忽略某些操作且如果区间内没有足够的黑块(黑块用于涂白),则不能进行这个操作。

分析:

写写画画一看就知道这道题是一个背包问题。

“恰好装满背包”。

以下摘自题解:

本题难点在于正确处理两种操作,不妨假设只有一种操作,那么这种操作如果是1的话那么就把操作按照a从小到大排序,每次都尽量往最左边涂,如果是2的话则类似的涂到最右边,但本题两种操作都出现了。

先考虑第一问:

我们把所有的操作按类别区分开,假设所有的1操作尽量用上能从1涂到a格子,所有的2操作尽量用上能从b格子涂到n,假设a<b,那么答案显然是a+n-b+1。那么假设a>=b,那么假设1操作从1涂到x,那么2操作一定会从n尽量往左边涂,直到x为止。最后两边的总和就是答案。

由上不难想到一个DP,l[i][j]表示用了前i种1操作,从1涂到j的最小操作数,转移l[i][j]=min(l[i][j],l[i-1][j-ope[i].x]+1)(ope[i].x<=j<=ope[i].a),类似的,我们可以得到r[i][j]表示用2操作从j涂到n需要的最小操作数。dp复杂度O(n*m)。(这个背包也可以压缩一维,后面的l,r均为1维的状态)

那么我们倒着枚举涂色最大的数目,假设为k,这个数目必然由1操作贡献一部分,由2操作贡献一部分(也可能一个贡献全部,另一个为0),那么我们枚举1操作涂到了i,那么2操作必然涂到了n-(k-i)+1,如果l[i]和r[n-(k-i)+1]均有值,那么说明这个最大数目是可达的,即是第一问的答案。枚举复杂度O(n*n)。

再考虑第二问:

现在我们已经知道最大数目了,这个数目是由操作1和操作2一起贡献的,那么我们仍然可以枚举操作1涂到了i,那么操作2涂到了n-(ans-i)+1,如果l[i]和r[n-(k-i)+1]均有值,那么其和可以用来更新第二问的答案,最后第二问的答案为所有合法方案需操作数的最小值。枚举复杂度O(n*n)。

代码实现:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cmath> using namespace std;
const int maxn = ;
struct node{
int aa,cor;
node(){}
node(int _aa,int _cor){
aa = _aa; cor = _cor;
}
}x1[maxn],x2[maxn];
int n,m;
int dp1[maxn],dp2[maxn];
int sumx1,sumx2; bool cmp(const node &p,const node &q){
return p.aa < q.aa;
} int min(int a,int b)
{
return a<b?a:b;
} int main()
{
int cas,ta=;
scanf("%d",&cas);
while(cas--){
int i,j;
scanf("%d%d",&n,&m); sumx1 = sumx2 = ;
for(i=; i<m; i++){
int key,yy,z;
scanf("%d%d%d",&key,&yy,&z);
if(key == ){
x1[sumx1++] = node(yy,z);
}else{
x2[sumx2++] = node(n+-yy,z);
}
}
sort(x1+,x1+sumx1,cmp);
sort(x2+,x2+sumx2,cmp);
memset(dp1,0x3f,sizeof(dp1));
memset(dp2,0x3f,sizeof(dp2)); dp1[] = dp2[] = ;
for(i=; i<sumx1; i++){
for(j=x1[i].aa; j>=x1[i].cor; j--){
dp1[j] = min(dp1[j],dp1[j-x1[i].cor]+);
}
} for(i=; i<sumx2; i++){
for(j=x2[i].aa; j>=x2[i].cor; j--){
dp2[j] = min(dp2[j],dp2[j-x2[i].cor]+);
}
} int ans = ,anssum = ,tmp;
for(i=; i<=n; i++){
for(j=; j<=i; j++){
tmp = dp1[j] + dp2[i-j];
if(tmp <= m){
if(ans != i){
ans = i; anssum = tmp;
}else if(tmp < anssum){
anssum = tmp;
}
}
}
} printf("Case %d: %d %d\n",ta++,ans,anssum);
}
return ;
}

最新文章

  1. kvm 简介
  2. XHTML的若干注意点
  3. arrhelper::map
  4. RF内置库-----内置库的学习过程总结
  5. 作业,备份,存储过程,sqlserver print 语句,输出字符串
  6. hping3
  7. Ubuntu下安装配置zsh和oh my zsh
  8. Hibernate之Session缓存以及操作Session缓存的相关方法
  9. jquery easyui将form表单元素的值序列化成对象
  10. AutoLayout框架Masonry使用心得
  11. POJ 1321-棋盘问题(DFS 递归)
  12. 自学nodejs系列
  13. vs2015下编译duilib的几个问题
  14. Java判断一个字符是否是数字的几种方法的代码
  15. Asp.Net MVC上传图片
  16. Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat
  17. 解决maven编译Java中的使用了未经检查或不安全的操作
  18. EF-生成迁移版本
  19. Create PDB with Sample schemas in 12C
  20. MySQL - 问题集 - Access denied; you need the SUPER privilege for

热门文章

  1. POJ 3710 Christmas Game
  2. Linux下find一次查找多个指定类型文件,指定文件或者排除某类文件,在 GREP 中匹配多个关键 批量修改文件名等
  3. sftp的安装和使用
  4. yafeilinux.com的开源项目非常好的东西
  5. DP 子序列问题
  6. sublime3运行lua
  7. Android 动画 setVisibility 后出错解决方法
  8. linux scp命令参数及用法详解--linux远程复制拷贝命令使用实例【转】
  9. java生成随机序列号
  10. 无锁编程(五) - RCU(Read-Copy-Update)