题意
一张宽度为
的纸,现在要插入
张图片,第
张图片的宽度为
,高度为
,插入的方法如下:
1.当这张图片可以在同行插入,则插至同行;
2.如果这一行宽度已满,调至下一行;
3.否则将图片等比例压缩填满此行(高度向上取整)。
如现在有
张图片:
宽度
为
将变成如下的图:
0123456789
----------
44
111 44
111 33344
1112233344
1112233344
5555555555
66666
66666777
66666777
66666777
66666777
现在需要舍弃一张图片使得总高度最小,求高度最小值。
思路
比较好想的方法是枚举舍弃哪一张图片,但是时间上不允许,考虑到填满某一行后,后面的插入会多次重复计算,可以倒序预处理出一个数组 , 表示 号图片及其后的图片另起一行插入得到的高度。然后按顺序枚举哪一张图片不选,同时将选的图片插入。预处理和求解可以共用一个“放满一行”的函数。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
int t[100003];
int n,m;
struct pic
{
int w,h;
void zipped(int res)
{
(*this)=(pic){res,(h*res+w-1)/w};
}
}a[100003];
struct line
{
int w,h;
line(){w=0,h=0;} //如果需要同时使用强转和构造,需要重载两个构造函数
line(int _w,int _h){w=_w,h=_h;}
line operator +(pic _)
{
if(w+_.w>m)
_.zipped(m-w);
return (line){w+_.w,max(h,_.h)};
}
bool full(){return w==m;}
void clr(){w=h=0;}
};
int push_till_full(line alpha,int k)
{
for(;k<=n && !alpha.full();k++)
alpha=alpha+a[k];
return t[k]+alpha.h;
}
int main()
{
int ans=2e9;
scanf("%d%d",&m,&n);
FOR(i,1,n)scanf("%d%d",&a[i].w,&a[i].h);
DOR(i,n,1)t[i]=push_till_full((line){0,0},i);
line alpha;
int per=0;
FOR(i,1,n)
{
ans=min(ans,per+push_till_full(alpha,i+1));
alpha=alpha+a[i];
if(alpha.full())
{
per+=alpha.h;
alpha.clr();
}
}
printf("%d\n",ans);
return 0;
}