将 \(a,b,c,d\) 分别用 \(a_1,a_2,a_3,a_4\) 表示。
令与第 \(i\) 类食物中的第 \(j\) 个冲突的第 \(i-1\) 类食物的集合为 \(S_{i,j}\)。
首先是是人都能看出来的 DP:
令 \(f_{i,j}\) 为选择第 \(i\) 类食物的第 \(j\) 个时所需的最小费用,显然有
\[f_{i,j}=\min_{k=1}^{k\le n_{i-1},k\notin S_{i,j}}f_{i-1,k}+a_{i,j}
\]
现在问题在于这个最小值怎么求。
容易发现,\(S_{i,j}\) 中的元素将序列 \(a_{i-1}\) 划分成了若干区间:
只要求出这些区间的最小值的最小值(不是语病 QAQ)就好了。
静态区间最小值?那自然是 ST 表!
使用 vector
存储上述的 \(S_{i,j}\),内部排序后,挨个遍历提取区间即可。
\(\min_{i=1}^{n_4}f_{4,i}\) 即为答案,如果它等于 INF
则证明无解。
时间复杂度显然 \(O(n\log n)\)。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define INF 0x3fffffff
using namespace std;
typedef long long ll;
constexpr int N=1.5e5+5;
int n[5],a[5][N],lg[N],minn[N][20];
//这里直接用a代替f了
void make(int *a,int n){//以a为原数组,建立ST表的函数
for(int i=1;i<=n;i++)minn[i][0]=a[i];
for(int i=1;i<=lg[n];i++)
for(int j=1;j+(1<<i)-1<=n;j++)
minn[j][i]=min(minn[j][i-1],minn[j+(1<<(i-1))][i-1]);
}
inline int query(int l,int r){
if(r<l)return INF;
int len=lg[r-l+1];
return min(minn[l][len],minn[r-(1<<len)+1][len]);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
lg[0]=-1;
for(int i=1;i<=4;i++)cin>>n[i];
for(int i=1;i<=4;i++)
for(int j=1;j<=n[i];j++)
cin>>a[i][j],lg[j]=lg[j/2]+1;
for(int i=1;i<=3;i++){
vector<int>unfit[N];
for(int j=1;j<=n[i+1];j++)unfit[j].push_back(0);//第0个和第n+1个肯定不能选
int m;cin>>m;
for(int j=1;j<=m;j++){
int x,y;cin>>x>>y;
unfit[y].push_back(x);
}
for(int j=1;j<=n[i+1];j++){
unfit[j].push_back(n[i]+1);
sort(unfit[j].begin()+1,unfit[j].end()-1);
//第一个和最后一个一定在正确位置,这样可以稍微减小一点常数(并没什么用
}
make(a[i],n[i]);//建立ST表
for(int j=1;j<=n[i+1];j++){
int minn=INF;
for(auto k=unfit[j].begin()+1;k!=unfit[j].end();k++)
minn=min(minn,query(*(k-1)+1,*k-1));//取区间最小值
a[i+1][j]=minn==INF?INF:a[i+1][j]+minn;//注意这里不能直接加,INF加任何数都得INF
}
}
int minn=INF;
for(int i=1;i<=n[4];i++)minn=min(minn,a[4][i]);
cout<<(minn==INF?-1:minn)<<endl;
return 0;
}
时限开了 4s,但是 C++ 就跑过了