虽然是一道水题,TLE+WA了一天,WA的原因是因为自己粗心了,DP的范围明明超过了int,强行用int来写。(不过很快就改过来了,然后T了一天)至于T的原因,只能说自己太蠢,HDU给了我一种错觉,总觉得C++跑得要比G++快,然后加了10发C++全T了,最后抱着试试看的心情交了一发G++,AC了。。。只跑了452ms。。。然后把第一次敲的代码放上去,也妥妥地AC了。。。唉。。。
首先,这道题的转移方程比较好写:dp[i][m] = min{dp[j][m-1]+w[i]*h[j+1]}。在这之前,我们需要把每一个矩形进行排序,按照w排(h排也行),然后w相同的按照h排,顺序都是从大到小,目的就是去掉w[j]<=w[i] && h[j]<=h[i]的矩形,因为这样的矩形没有任何意义。
然后对于: i > j > K:
G(j,k) = (dp[j][m-1] - dp[k][m-1])/(h[k+1]-h[j+1]) < w[i] :j点比k点优,k点可以删除,因为w[i]单调的,所以后面的点,j点总是比k点优。
G(j,k) >= G(i,j):则j点可以删除。
这道题由一维变成了二维,其实,只需要用队列维护dp[i][pre]的凸包就好了。
因为对内存要求不是很高,所以直接搞就好了,如果题目对内存有要求,那么最好换成滚动数组来写。
下面附上AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <queue>
#include <stack>
#define ll long long
#define FOR(i,x,y) for(int i = x;i < y;i ++)
#define N 55555
#define K 111
using namespace std;
typedef pair<ll,ll> P;
ll dp[N][K];
int n,k,cnt;
ll w[N],h[N],w_tem[N],h_tem[N],q[N],tail,head;
P ret[N];
ll G_UP(int i,int j,int k){
return (dp[i][k]-dp[j][k]);
}
ll G_DOWN(int i,int j){
return (h[j+1]-h[i+1]);
}
ll G_DP(int i,int j,int k){
return dp[j][k]+w[i]*h[j+1];
}
bool cmp(P a,P b){
if(a.first < b.first) return true;
if(a.first == b.first && a.second < b.second) return true;
return false;
}
void init(){
cnt = 0;
w_tem[cnt] = ret[n-1].first;
h_tem[cnt++] = ret[n-1].second;
for(int i = n-2;i >= 0;i--){
if(ret[i].second <= h_tem[cnt-1]) continue;
w_tem[cnt] = ret[i].first;
h_tem[cnt++] = ret[i].second;
}
FOR(i,0,cnt){
w[i] = w_tem[cnt-1-i];
h[i] = h_tem[cnt-1-i];
}
FOR(i,0,cnt){
dp[i][1] = w[i] * h[0];
}
}
void solve(){
if(k > cnt) k = cnt;
FOR(m,2,k+1){
head = tail = 0;
q[tail++] = m-2;
for(int i = m-1;i < cnt;i ++){
while(head+1 < tail && (G_UP(q[head+1],q[head],m-1)) <= (G_DOWN(q[head+1],q[head])*w[i])) head++;
dp[i][m] = G_DP(i,q[head],m-1);
while(head+1 < tail && (G_UP(q[tail-1],q[tail-2],m-1)*G_DOWN(i,q[tail-1]) >= G_DOWN(q[tail-1],q[tail-2])*G_UP(i,q[tail-1],m-1)))
tail--;
q[tail++] = i;
}
}
}
int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d",&n,&k)){
ll x,y;
FOR(i,0,n){
scanf("%I64d%I64d",&x,&y);
ret[i] = make_pair(x,y);
}
sort(ret,ret+n,cmp);
init();
solve();
ll ans = dp[cnt-1][1];
FOR(i,2,k+1){
ans = min(ans,dp[cnt-1][i]);
}
printf("%I64d
",ans);
}
return 0;
}
附上滚动数组版:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <queue>
#include <stack>
#define N 50010
#define K 105
using namespace std;
typedef long long ll;
struct Node{
ll x,y;
}ret[N];
ll dp[N][2];
int n,k,cnt;
ll w[N],h[N],w_tem[N],h_tem[N],q[N],tail,head;
ll ans;
ll G_UP(int i,int j,int k){
return (dp[i][k]-dp[j][k]);
}
ll G_DOWN(int i,int j){
return (h[j+1]-h[i+1]);
}
ll G_DP(int i,int j,int k){
return dp[j][k]+w[i]*h[j+1];
}
bool cmp(Node a,Node b){
if(a.x < b.x) return true;
if(a.x == b.x && a.y < b.y) return true;
return false;
}
void init(){
cnt = 0;
w_tem[cnt] = ret[n-1].x;
h_tem[cnt++] = ret[n-1].y;
for(int i = n-2;i >= 0;i--){
if(ret[i].y <= h_tem[cnt-1]) continue;
w_tem[cnt] = ret[i].x;
h_tem[cnt++] = ret[i].y;
}
for(int i = 0;i < cnt;i ++){
w[i] = w_tem[cnt-1-i];
h[i] = h_tem[cnt-1-i];
}
for(int i = 0;i < cnt;i ++){
dp[i][0] = w[i] * h[0];
}
ans = dp[cnt-1][0];
}
void solve(){
if(k >= cnt) k = cnt;
int pre = 0,cur = 1;
for(int m = 2;m <= k;m ++){
head = tail = 0;
q[tail++] = m-2;
for(int i = m-1;i < cnt;i ++){
while(head+1 < tail && (G_UP(q[head+1],q[head],pre)) < (G_DOWN(q[head+1],q[head])*w[i])) head++;
dp[i][cur] = G_DP(i,q[head],pre);
while(head+1 < tail && (G_UP(q[tail-1],q[tail-2],pre)*G_DOWN(i,q[tail-1]) >= G_DOWN(q[tail-1],q[tail-2])*G_UP(i,q[tail-1],pre)))
tail--;
q[tail++] = i;
}
ans = min(ans,dp[cnt-1][cur]);
cur = 1-cur;
pre = 1-pre;
}
}
int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d",&n,&k)){
for(int i = 0;i < n;i ++){
scanf("%I64d%I64d",&ret[i].x,&ret[i].y);
}
sort(ret,ret+n,cmp);
init();
solve();
printf("%I64d
",ans);
}
return 0;
}