题目
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问题大题-优快云博客
其实一看到数据范围就想到这个了,但是没有想到怎么套这个
赛后看了别人的做法之后秒懂了,加一个「多路归并」
考虑朴素转移的怎么做,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;
}