题意
给定n个从小到大的数组,从每个数组中选出一个下标构成一个序列,但是有m种序列被ban了,要求选出的序列对应的数字和最大且没有被ban。
思路
因为有m个序列被ban了,那么最坏的情况可以假设为最大的m个序列被ban了,那么要求的序列就是第m+1大的序列,优先队列+bfs求前m+1大的序列就好了,在求的中途碰到没有被ban的序列直接退出输出即可。查询一个序列是否被ban用hash的思想,用map+vector的方式查询一个序列是否被ban。还是不好懂的看代码比较清晰=3=.
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[13][200005];
int c[13];
typedef pair<int,vector<int> > prefix;//first存储该序列的和,vector存储序列
struct cmp//优先队列用
{
bool operator()(const prefix p1,const prefix p2)
{
return p1.first<p2.first;
}
};
priority_queue<prefix,vector<prefix>,cmp >q;//大根堆优先队列存储序列
map<vector<int>,bool>mp;//查询一个序列是否被ban,mp[v]=1,则v这个序列被ban了
pair<int,vector<int> >ans;//存储答案,int为答案的和大小,vector存储答案序列
map<vector<int>,bool>mp2;//bfs中途判断这个序列有没有走过,比如222 可以由322,232,223走到,相当于一般bfs的vis数组
void bfs()//bfs+优先队列求没被ban的最大的序列
{
while(q.size())
{
ans=q.top();//先给ans赋值当前优先队列中最大的序列
q.pop();
mp2[ans.second]=1;//这个序列被走过
if(!mp[ans.second])//如果这个序列没被ban则退出
break;
for(int i=1;i<=n;i++)//bfs每个走一步能变成的序列
{
int sum=ans.first;//存储当前序列和
vector<int>v=ans.second;//存储当前序列
if(v[i-1]>1)//如果当前序列第i个大于1说明还可以-1,那么对他进行-1加入队列操作
{
sum-=(a[i][v[i-1]]-a[i][v[i-1]-1]);//更新和
v[i-1]--;//第i个-1
pair<int,vector<int> >tmp;
tmp.first=sum;
tmp.second=v;
if(!mp2[tmp.second])//判断这个序列有没有走过
{
q.push(tmp);
mp2[tmp.second]=1;
}
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)//输入n个数组
{
scanf("%d",&c[i]);
for(int j=1;j<=c[i];j++)
{
scanf("%d",&a[i][j]);
}
}int m;scanf("%d",&m);
for(int i=1;i<=m;i++)//输入m个被ban的序列
{
vector<int>v;
for(int j=1;j<=n;j++)
{
int t;
scanf("%d",&t);
v.push_back(t);
}
mp[v]=1;
}
//给ans赋值当前最大的序列并加入优先队列
ans.first=0;
vector<int>v1;
for(int i=1;i<=n;i++)
{
v1.push_back(c[i]);
ans.first+=a[i][c[i]];
}
ans.second=v1;
q.push(ans);
bfs();//bfs求最大的没被ban的序列
vector<int>v2;
v2=ans.second;
for(int i=0;i<n;i++)//输出答案
{
printf("%d ",v2[i]);
}
printf("\n");
return 0;
}