省选专练POI2015 Myjnie

23 篇文章 0 订阅
20 篇文章 0 订阅

毒瘤DP

什么?你说这TM就是个提高题?

我好菜啊

冷静思考:

当我们考虑洗车的老哥时,一个洗衣店有明显的后效性:最大费用可行流 动态规划。

f(i,j,k)表示:区间i,j,价值不小于k的答案max的后缀。

pre(i,j,k)表示:区间i,j此时答案的实际k

fat(i,j,k)表示:区间i,j此时答案实际k的实际位置。

cnt(i,k)表示:i位置最大值可满足为k的后缀和

四个dp滚动维护

真是简单的提高组难度的题啊

#include<bits/stdc++.h>
using namespace std;
typedef int INT;
#define int int
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
	x=0;
	int f=1;
	char ch=nc();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-1;
		ch=nc();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=nc();
	}
	x*=f;
}
const int N=51;
const int M=4001;
int n,m;
struct Node{
	int l,r,c;
}a[M];
int mmp[M]={};
int cnt[N][M]={};
int dp[N][N][M]={};
int pre[N][N][M]={};//the actually k 
int fat[N][N][M]={};//the k's pos
int ans[N]={};
void expend(int l,int r,int k){
//	cout<<l<<" "<<r<<" "<<fat[l][r][k]<<'\n';
	k=pre[l][r][k];
	ans[fat[l][r][k]]=mmp[k];
	if(l==r)
		return;
	if(fat[l][r][k]>l)expend(l,fat[l][r][k]-1,k);
	if(fat[l][r][k]<r)expend(fat[l][r][k]+1,r,k);
}
INT main(){
	read(n);
	read(m);
	for(int i=1;i<=m;i++){
		read(a[i].l);
		read(a[i].r);
		read(a[i].c);
		mmp[i]=a[i].c; 
	} 
	sort(mmp+1,mmp+1+m);
	int len=unique(mmp+1,mmp+1+m)-1-mmp;
	for(int i=1;i<=m;i++){
		a[i].c=lower_bound(mmp+1,mmp+1+len,a[i].c)-mmp;
	}
	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<=len;j++){
					cnt[i][j]=0;
				}
			}
			for(int i=1;i<=m;i++){
				if(L<=a[i].l&&a[i].r<=R){
					for(int j=a[i].l;j<=a[i].r;j++){
						cnt[j][a[i].c]++;
					}
				}
			}
			for(int i=len-1;i>=1;i--){
				for(int j=L;j<=R;j++){
					cnt[j][i]+=cnt[j][i+1];
				}
			}
			pre[L][R][len]=len;
			for(int k=len;k>=1;k--){
				int max_sum=-1;
				int pos;
				for(int i=L;i<=R;i++){
					int l_sum=0;
					int r_sum=0;
					if(L<i)l_sum=dp[L][i-1][k];
					if(i<R)r_sum=dp[i+1][R][k];
					int sum=cnt[i][k]*mmp[k]+l_sum+r_sum;
					if(sum>max_sum)max_sum=sum,pos=i; 
				}
				dp[L][R][k]=max_sum;
				fat[L][R][k]=pos;
				if(k<len){
					if(dp[L][R][k]<dp[L][R][k+1]){
						dp[L][R][k]=dp[L][R][k+1];
						pre[L][R][k]=pre[L][R][k+1];
					}
					else pre[L][R][k]=k;
				}
			}
		}
	}
	cout<<dp[1][n][1]<<'\n';
	/*for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)fat[i][i][j]=i;*/
	expend(1,n,1);
//	for(int i=1;i<=n;i++)
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<" ";
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值