CodeTON Round 8 (Div. 1 + Div. 2, Rated, Prizes!) D. Learning to Paint(思维题/优先队列)

题目

n(n<=1e3)个格子,每个格子可以涂色也可以不涂色,共2^n种涂法,

对于每一种涂法,把涂色且相邻的位置合并成同一个区间,

此外,还有一个n*n的数组a,第i行第j列为a[i][j](-1e6<=a[i][j]<=1e6),用来评估得分,

例如,对如上涂色方式,实际得分为a[2][4]+a[6][6]+a[8][9],

求得分最大的k(1<=k<=min(5000,2^n))种得分

实际为t(t<=1e3)组样例,但保证sumn不超过1e3,sumk不超过5e3

思路来源

HHY_zZhu代码

题解

UVA11997 K Smallest Sums(思维题/优先队列)_k2问题大题-CSDN博客

其实一看到数据范围就想到这个了,但是没有想到怎么套这个

赛后看了别人的做法之后秒懂了,加一个「多路归并」

考虑朴素转移的怎么做,dp[i][j]表示考虑前i个位置(第i个位置可以不取)前j大的分数和是多少

考虑将第i+1个加入的时候,枚举第i+1的区间向左到哪里,比如到x,

那[x,i+1]是新加入的区间,x-1是无法取的,不然会连成同一个区间,

x-2往前是可以任意取的,这种取法对应的最大值就是dp[x-2][1],

对于x∈[1,i+1]的这些情况,把每种的top1塞入优先队列,然后就可以多路归并了,

每次取出优先队列里的最大值,并把(下一个rank对应的值,位置,下一个rank)塞入优先队列

重复如上过程,直至取够第i+1个位置的前min(k,5000)大

朴素转移是需要考虑三种情况的,

1. 没有前驱区间,即x-2<=0

2. 本次不取第i+1,即原来的top1,本次什么都不加,也需要塞入优先队列

3. 加上一个[x,i+1]的区间,再塞入优先队列

然后,不妨调整一下dp值的含义,兼容一下这三种情况

dp[i][j]表示考虑[1,i-1],i强制不取时,前j大的分数和,

for循环遍历1到n+1,每次往优先队列里放入top1即可,即dp[j][1]+a[j+1][i-1]

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<set>
#include<queue>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=1e3+10,M=5e3+10,INF=0x3f3f3f3f;
int t,n,k;
int a[N][N],dp[N][M];//dp[i][j]表示考虑[1,i-1] i强制不取时 前j大的分数和
struct node{
	int v,pos,rk;
};
bool operator<(node c,node d){
	return c.v<d.v;
}
int main(){
    sci(t);
    while(t--){
        sci(n),sci(k);  
        rep(i,1,n){
            rep(j,i,n){
                sci(a[i][j]);
            }
        }
        rep(i,0,n+1){
            rep(j,0,k){
                dp[i][j]=-INF;
            }
        }
        dp[0][1]=0;
        rep(i,1,n+1){
            priority_queue<node>q;
            per(j,i-1,0){
                q.push({dp[j][1]+a[j+1][i-1],j,1});//j+1>i-1时表示不考虑选i 恰好统一
            }
            rep(j,1,k){
                if(q.empty())break;
                node x=q.top();q.pop();
                dp[i][j]=x.v;
                int v=x.v,p=x.pos,rk=x.rk;
                if(rk+1<=k && dp[p][rk+1]>-INF)q.push({dp[p][rk+1]+a[p+1][i-1],p,rk+1});
            }
        }
        rep(i,1,k){
            printf("%d%c",dp[n+1][i]," \n"[i==k]);
        }
	}
	return 0;
}

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值