总结
第一题:n
只有4
,直接暴力
第二题:Tarjan缩点之后跑一个最长路
第三题:DP
第四题:思维量较大
A、翻转游戏
题目描述
分析
\(n\)的范围很小,所以我们考虑状压DP
我们设\(f[i][j][k][m]\)为第一行的状态为\(i\),第二行的状态为\(j\),第三行的状态为\(k\),第四题的状态为\(m\)所需要的最小步数
所以我们暴力枚举6重循环,时间复杂度为\(15 \times 15 \times 15 \times 15 \times 4 \times 4\)
考场95分代码
#include
using namespace std;
const int maxn=5;
int f[1<
100分代码
因为我们的状态不是从0
开始的,所以我们不能只从状态为0
正着开始枚举
比如下面这一组数据
bbbb
bwwb
bwwb
bbbb
正解应该输出4,但是上面的代码却输出6
解决这一个问题,我们倒着枚举一遍就可以了
#include
using namespace std;
const int maxn=5;
int f[1<=0;i--){
for(int j=(1<<4)-1;j>=0;j--){
for(int k=(1<<4)-1;k>=0;k--){
for(int m=(1<<4)-1;m>=0;m--){
for(int d=1;d<=4;d++){
if(d==1){
for(int n=1;n<=4;n++){
if(n==1){
int now=i^(1<<(n-1));
now=now^(1<
B、抢掠计划
题目描述
分析
显然,处于同一个强连通分量的点可以互相到达
所以我们将每一个强连通分量分别缩点
缩完点后的图就变成了一个有向无环图
这时,我们就可以用SPFA求出最长路
100分代码
#include
using namespace std;
typedef long long ll;
const int maxn=2e6+5;
int head[maxn],tot=1;
struct asd{
int from,to,next;
ll val;
}b[maxn],b2[maxn];
void ad(int aa,int bb,ll cc){
b[tot].from=aa;
b[tot].to=bb;
b[tot].next=head[aa];
b[tot].val=cc;
head[aa]=tot++;
}
int h2[maxn],t2=1;
void ad2(int aa,int bb){
b2[t2].from=aa;
b2[t2].to=bb;
b2[t2].next=h2[aa];
h2[aa]=t2++;
}
int dfn[maxn],low[maxn],dfnc,sta[maxn],top;
int js,shuyu[maxn],siz[maxn];
ll a[maxn],ans[maxn];
void tar(int xx){
dfn[xx]=low[xx]=++dfnc;
sta[++top]=xx;
for(int i=h2[xx];i!=-1;i=b2[i].next){
int u=b2[i].to;
if(!dfn[u]){
tar(u);
low[xx]=min(low[xx],low[u]);
} else if(!shuyu[u]){
low[xx]=min(low[xx],dfn[u]);
}
}
if(dfn[xx]==low[xx]){
js++;
while(sta[top]!=xx){
int y=sta[top--];
shuyu[y]=js;
siz[js]++;
}
top--;
shuyu[xx]=js;
siz[js]++;
}
}
queue q;
int vis[maxn];
ll dis[maxn];
void SPFA(int xx){
q.push(xx);
vis[xx]=1;
while(!q.empty()){
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
if(dis[u]
C、测绘
题目描述
分析
这一道题非常显然是一道DP题
我们设\(f[i][j]\)为前\(i\)个数中已经选择了\(j\)个数的价值,并且处于\(i\)位置上的数一定选择
那么就有$ f[i][j]= \min (f[i][j],f[k][j-1]+sum[k][i]);\( 其中\)j-1 \leq k < i\( \)sum[i][j]\(为选走\)i,j\(位置上的数,区间\)[i,j]\(中的数产生的误差 这一道题要注意初始化和最后的处理 我们需要将\)f[i][1]\(和\)f[i][i]\(预处理一下 同时,当\)DP$结束后,因为最右边的价值没有计算,所以我们需要把最右边的价值加上
100分代码
#include
using namespace std;
typedef long long ll;
const int maxn=105;
int n,vis[maxn],jud;
ll e,a[maxn],sum[maxn][maxn],f[maxn][maxn];
int main(){
memset(f,0x3f,sizeof(f));
scanf("%d%lld",&n,&e);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int k=i+1;k
D、奖学金
分析
数据范围高达 10 万,显然至少是\(O(n∗log(n))\) 才能通过。
我们分析性质中位数 \(ai\) 必须满足:\(n2+1≤i≤C−n2\) 。
当 \(i=n2+1\) 时,我们必须选上最小分数最低的前 \(n2\) 的猪。
所以我们可以枚举每一个中位数,用一个维护奖金的大根堆,每枚举完一个中位数,如果当前的奖金比堆顶的小,则交换,始终保证堆的有 \(n2\) 个数,同时用一个数组 \(f[i]\) 维护如果选 \(ai\) 为中位数,前 \(n2\) 个数的最小奖金。
同上,倒序维护,求出 \(g[i]\) 表示,如果选 \(ai\) 为中位数,则后 \(n2\) 个数最小奖金。
显然答案为满足 \(f[i]+g[i]+a[i].w<=F\) 的最大的 \(a[i].s\) 。
代码
#include
const int maxn=2e5+5;
int n,c,F;
std::priority_queue q;
struct Node{
int s,w;//分数,奖金
} a[maxn];
bool cmp(const Node &a, const Node &b){
return a.sa[i].w){//如果当前的奖金小于堆顶则交换掉
q.pop();
sum-=top;
sum+=a[i].w;
q.push(a[i].w);
}
}
sum=0;
while(!q.empty()) q.pop();
for(int i=c;i>=c-n/2+1;--i){//成绩最高的n/2进入队列
sum+=a[i].w;
q.push(a[i].w);
}
//g[i]:表示以i为中位数后n/2人的最低奖金
for(int i=c-n/2;i>=1;--i){
g[i]=sum;
int top=q.top();
if(top>a[i].w){//交换
q.pop();
sum-=top;
sum+=a[i].w;
q.push(a[i].w);
}
}
//中位数的取值范围是[n/2+1,c-n/2]
//因为要求最大中位数,所以倒序
for(int i=c-n/2;i>=n/2+1;--i)
if(a[i].w+f[i]+g[i]<=F){
printf("%d", a[i].s);
return;
}
printf("-1\n");
}
int main(){
Init();
Solve();
return 0;
}