最优卡组
有 $k$ 个卡包,第 $i$ 个卡包里有 $c_i$ 张卡,每张卡有一个能力值,其中第 $i$ 个卡包里的第 $j$ 张卡具有 $a_{i, j}$ 点能力值。
他准备选择 $k$ 张卡牌的组合,其中每个卡包要选择恰好一张卡牌。他希望这 $k$ 张卡牌的能力值之和尽量大,请你告诉他在所有可能的组合里,能力值之和最大的 $n$ 个组合分别具有多大的能力值。
第一行包含两个整数 $k$ 和 $n$,含义见题目描述。
接下来 $k$ 行,每行描述一个卡包的信息,其中第 $i$ 行的第一个整数表示 $c_i$,接下来该行会有 $c_i$ 个整数,依次表示这个卡包里每张卡的能力值。
输出一行,包含 $n$ 个整数,相邻两个数字之间用空格隔开,其中第 $i$ 个整数表示第 $i$ 大的能力值之和。
思路
这道题真的是道毒瘤题目。。
神仙贪心思路
每一种方案可以表示为x~1~x~2~x~3~....x~n~
x~i~为第i个数组的第x~i~大
然后有三条规则
我们先找到最后一个不为1的x~i~
之后把这个x~i~+1或者将x~i+1~+1
如果x~i~为2
则可以将x~i~-1并且 x~i+1~+1入堆
为什么这条规则是正确的呢?
首先容易证明用了这三条规则所有的情况都可以枚举到
接着证明所有的情况只会枚举到一次
证明1
用这三条规则,
我们可以容易发现每一个状态其他状态的转移相关的只是最后一个不为1的x~i~
也就是说我们设第一个状态x~1~x~2~,第二个状态 y~1~y~2~
也就是说我们用这两个状态用规则进行转移可以列6个方程
对这6个方程进行求解,可以发现$x _1==y _1$并且$x_2==y_2$或者是方程无解
即结论成立
接着就是顺序的问题
也就是说对于出堆的一个状态,所有比它大的状态都已经出堆
证明2
将命题用另一个方式说:所有比它大的状态都在它出堆之前入堆
之后。。。
很容易就证明了
代码
#include<iostream> #include<algorithm> #include<vector> #include<queue> using namespace std; #define int long long struct node { long long w; int x,y,z; friend bool operator < (const node &a,const node &b) { return a.w<b.w; } }; int n,k; int s=0; int id[300005]; vector<int> v[300005]; priority_queue<node> q; bool cmp1(int a,int b) { return a>b; } bool cmp2(int a,int b) { return v[a][0]-v[a][1]<v[b][0]-v[b][1]; } signed main() { ios::sync_with_stdio(false); cin>>n>>k; for(int i=1;i<=n;i++) { int siz; cin>>siz; if(siz==1) { int x; cin>>x; s+=x; n--; i--; continue; } for(int j=1;j<=siz;j++) { int x; cin>>x; v[i].push_back(x); } sort(v[i].begin(),v[i].end(),cmp1); } for(int i=1;i<=n;i++) id[i]=i; sort(id+1,id+n+1,cmp2); for(int i=1;i<=n;i++) s+=v[i][0]; q.push((node){s,1,1,0}); for(int i=1;i<=k;i++) { node t=q.top(); q.pop(); cout<<t.w<<' '; if(t.y<v[id[t.x]].size()) q.push((node){t.w-v[id[t.x]][t.y-1]+v[id[t.x]][t.y],t.x,t.y+1,0}); if(t.x<n) q.push((node){t.w-v[id[t.x+1]][0]+v[id[t.x+1]][1],t.x+1,2,1}); if(t.x<n&&t.z==1) q.push((node){t.w-v[id[t.x]][1]+v[id[t.x]][0]-v[id[t.x+1]][0]+v[id[t.x+1]][1],t.x+1,2,1}); } return 0; }