BZOJ4380: [POI2015]Myjnie

138 篇文章 0 订阅
77 篇文章 0 订阅

因为每个人不洗车的条件是最小价格大于c[i],所以易知最后每家店的价格都是c[i]一定有一种方案使得所有人花的钱总和最大
那么就和c[i]的具体数值无关,先把它离散化
然后因为一个人决策的标准和区间的最小值有关,将c[i]引入状态定义,
定义f[i][j][c]代表 ij 的最小值为 c ,这个区间包含的人花的钱最多是多少,枚举左端点DP

于是枚举ij中的某个点 k 的值为c进行转移,尝试写转移方程时,发现需要知道 k ij中的多少个人包含,于是还要维护一个 h[k][c] 表示对于当前DP区间,经过k且价格要求大于等于c的有多少个人
那么写出方程

f[i][j][c]=max(max(f[i][k1][c2],c2>=c)+max(f[i][k+1][c3],c3>=c)+ch[k][c])

发现仍然不太好转移,所以还要维护一个 g[i][j][c] 表示 max(f[i][j][k],k>=c)
维护了这三个后就可以DP求解了

答案就是 g[1][n][1] ,然后要求输出方案,可以在转移的时候记录每个 f <script type="math/tex" id="MathJax-Element-13">f</script>是哪个位置取了c值,搜一下就好了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 55;
const int maxm = 4100;
const int maxc = 510000;

struct node
{
    int a,b,c;
}a[maxm];
inline bool cmp(node x,node y){return x.b==y.b?x.a<y.a:x.b<y.b;}

int f[maxn][maxn][maxm],g[maxn][maxn][maxm],h[maxn][maxm];
int p[maxn][maxn][maxm],val[maxn];
int C[maxm],lsh[maxc],LSH[maxm],K;
int n,m;

void pre()
{
    sort(a+1,a+m+1,cmp);

    sort(C+1,C+m+1); K=0;
    for(int i=1;i<=m;i++)
        if(C[i]!=C[i-1]) LSH[++K]=C[i],lsh[C[i]]=K;
    for(int i=1;i<=m;i++) a[i].c=lsh[a[i].c];
}
void search(int l,int r,int k)
{
    if(l>r) return ;
    if(!g[l][r][k])
    {
        for(int i=l;i<=r;i++) val[i]=LSH[k];
        return ;
    }
    for(int i=k;i<=K;i++) if(g[l][r][k]==f[l][r][i])
    {
        const int pos=p[l][r][i];
        val[pos]=LSH[i];
        search(l,pos-1,i); search(pos+1,r,i);
        break;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) 
        scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),C[i]=a[i].c;

    pre();

    for(int l=n;l>=1;l--) for(int r=l;r<=n;r++)
    {
        for(int i=l;i<=r;i++) for(int j=1;j<=K;j++) h[i][j]=0;
        for(int i=1;i<=m&&a[i].b<=r;i++)
        {
            if(a[i].a<l) continue;
            for(int j=a[i].a;j<=a[i].b;j++) h[j][a[i].c]++;
        }
        for(int i=l;i<=r;i++) for(int j=K-1;j>=1;j--) h[i][j]+=h[i][j+1];

        for(int i=l;i<=r;i++)
        {
            for(int j=K;j>=1;j--)
            {
                int tmp=g[l][i-1][j]+g[i+1][r][j]+h[i][j]*LSH[j];
                if(f[l][r][j]<tmp) f[l][r][j]=tmp,p[l][r][j]=i;
                g[l][r][j]=g[l][r][j+1]>f[l][r][j]?g[l][r][j+1]:f[l][r][j];
            }
        }
    }
    printf("%d\n",g[1][n][1]);
    search(1,n,1);
    for(int i=1;i<=n;i++) printf("%d ",val[i]);

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值