目的是改名emm,我看我要写多久
1.消除游戏
考点: 记忆化搜索,合并操作。(这道题的合并操作是值得学习的!!!)
代码:
int Dfs(int From,int To,int p)
{
if (f[From][To][p]!=inf) return f[From][To][p];
if (From>To) return 0;
if (From==To) return m-p-1;
int tmp=inf;
if (p==m-1) tmp=min(tmp,Dfs(From+1,To,0));
else tmp=min(tmp,Dfs(From,To,p+1)+1);
for (int i=From+1;i<=To;i++)
if (a[i]==a[From]) tmp=min(tmp,Dfs(From+1,i-1,0)+Dfs(i,To,min(p+1,m-1)));
return f[From][To][p]=tmp;
}
- 如果将状态中的p放在 [ i , j ] [i,j] [i,j]里面是否好做?
2.草地排水
题目大意: 给你一些带权线段,数量为
n
n
n,左端点为
a
i
a_i
ai,右端点为
b
i
b_i
bi,权值是
c
i
c_i
ci。每条线段只有一次选择机会,没有选择限制。求在线段不重复的前提下,能得到的最大值。
考点: 离散化(unique用完要 -1!!!),最大值维护(我用的是单调队列,但是这里也可以用树状数组维护。听了评讲才知道原来两个都可以不用emm)
代码:
int t=unique(a+1,a+1+cnt)-a-1;//考场上没有-1死于非命QAQ
for (int i=1;i<=t;i++)
{
if (v[i].size())
{
ll tmp=f[q.front()];
for (int j=0;j<v[i].size();j++)
f[v[i][j].To]=max(f[v[i][j].To],tmp+v[i][j].Cost);
}
if (mark[i])
{
//打一个标记,只把线段的末端统计进来,这样就避免了用别人的左端更新自己的右端情况。
ans=max(ans,f[i]);
while (q.size()&&f[q.back()]<f[i]) q.pop_back();q.push_back(i);
}//其实这样的话还有一个用处,避免交叉
}
3.2019.11.5集训第二题
题目大意:现在有1到n个数,第i数有上下界
[
1
,
a
i
]
[1,a_i]
[1,ai]。现在把他们放在一个环上,使得任意相邻两个数选择的数值是不一样的。求方案数。
数据范围:
n
≤
1
0
6
,
a
i
≤
1
0
9
n\leq10^6,a_i\leq10^9
n≤106,ai≤109
题解:
我们先把它放到链上讨论:
定义状态
f
i
f_i
fi表示讨论到第i个数的方案数。那么就有
f
i
=
f
i
−
1
∗
a
i
f_i=f_{i-1}*a_i
fi=fi−1∗ai
但是我们加多了,没有考虑
a
i
a_i
ai与
a
i
−
1
a_{i-1}
ai−1相等的情况,重新改写式子
f
i
=
f
i
−
1
∗
a
i
−
f
i
−
2
∗
m
i
n
(
a
i
,
a
i
−
1
)
f_i=f_{i-1}*a_i-f_{i-2}*min(a_i,a_{i-1})
fi=fi−1∗ai−fi−2∗min(ai,ai−1)
但是我们又减多了,没有考虑
a
i
−
1
a_{i-1}
ai−1与
a
i
−
2
a_{i-2}
ai−2相等的情况,所以
f
i
=
f
i
−
1
∗
a
i
−
[
f
i
−
2
∗
m
i
n
(
a
i
,
a
i
−
1
)
−
f
i
−
3
∗
m
i
n
(
a
i
,
a
i
−
1
,
a
i
−
2
)
]
f_i=f_{i-1}*a_i-[f_{i-2}*min(a_i,a_{i-1})-f_{i-3}*min(a_i,a_{i-1},a_{i-2})]
fi=fi−1∗ai−[fi−2∗min(ai,ai−1)−fi−3∗min(ai,ai−1,ai−2)]。
打开过后就是
f
i
=
f
i
−
1
∗
a
i
−
f
i
−
2
∗
m
i
n
(
a
i
,
a
i
−
1
)
+
f
i
−
3
∗
m
i
n
(
a
i
,
a
i
−
1
,
a
i
−
2
)
f_i=f_{i-1}*a_i-f_{i-2}*min(a_i,a_{i-1})+f_{i-3}*min(a_i,a_{i-1},a_{i-2})
fi=fi−1∗ai−fi−2∗min(ai,ai−1)+fi−3∗min(ai,ai−1,ai−2)。
那么我们就有:
f
[
i
]
=
∑
j
=
0
i
f
[
j
]
∗
m
i
n
(
a
[
j
+
1...
i
]
)
∗
(
−
1
)
i
+
j
f[i]=\sum^{i}_{j=0}{f[j]*min(a[j+1...i])*(-1)^{i+j}}
f[i]=j=0∑if[j]∗min(a[j+1...i])∗(−1)i+j
对它进行化简可以得到
f
[
i
]
=
(
−
1
)
i
∑
j
=
0
i
f
[
j
]
∗
m
i
n
(
a
[
j
+
1...
i
]
)
∗
(
−
1
)
j
f[i]=(-1)^i\sum^{i}_{j=0}{f[j]*min(a[j+1...i])*(-1)^{j}}
f[i]=(−1)ij=0∑if[j]∗min(a[j+1...i])∗(−1)j
现在我们来考虑在环上的情况,定义
g
i
g_i
gi表示有i个元素的环的方案数。环与链的不同在于环要考虑首尾不同的情况。所以我们只需要从链中减去最后一个等于第一个的情况就可以了。
仔细思考一下,我们能够发现,如果最后一个与第一个相同,我们把两者合并一下就变成了长度为i-1的环。所以
g
i
=
f
i
−
g
i
−
1
g_i=f_i-g_{i-1}
gi=fi−gi−1
有一个小细节:我们要从环上最小的一个点开始我们的计算
原因在于,如果从较大的数开始讨论,讨论到最小的值时可能就没有数可以选择了。
考点:由环到链,由链到环;单调栈;
2019.11.13晚上的C题
今日份的大吉owo
题目大意:
现在给你一个n*m的矩阵,你需要从(1,1)走到(n,m);但是邪恶的索隆大魔王已经预测到你的行程,于是祂决定在其余的格子上派兵。由于怪物来自同一个军队,所以他们的体力是相同的,但是每个怪物由于战斗经历不同,造成的攻击也就不一样。如果你在当前方格碰上了他们,除非当前格子的怪物死去,否则你是不能通过的。
不过好在智慧的灰袍甘道夫在你临行前送了你两份法宝——一把魔法手杖!以及无限体力药水。魔法手杖的攻击力是一定的,你可以用它攻击你当前所在格子的怪物,为了方便讨论。我们把它设计成回合制战斗,保证你是先手。虽然你有魔法药水,但是受到伤害还是会觉得很痛,所以你希望到达终点时受到的伤害尽可能的少。
为了让这道题更有趣一些,魔法手杖进化出了特殊技能——使用该技能会使你接下来 s次移动,每一次移动后增加等同于移动到的格子的怪物的攻击力,s 次移动后,魔法手杖攻击力恢复至初始攻击力。但是有使用限制,而且必须要等上一个技能使用完,再使用下一个(不然就太没有意思啦)
数据范围
1
≤
n
,
m
≤
500
,
1
≤
t
≤
10
,
1
≤
k
≤
5
1\leq n,m\leq500,1\leq t\leq10,1\leq k\leq5
1≤n,m≤500,1≤t≤10,1≤k≤5
1
≤
a
t
k
≤
100
,
1
≤
1\leq atk\leq100,1\leq
1≤atk≤100,1≤ 怪物攻击力
≤
100
\leq100
≤100
思路部分:
不用思索就知道是多维Dp,先定状态
f
[
i
]
[
j
]
[
k
]
[
t
]
[
s
]
[
m
]
f[i][j][k][t][s][m]
f[i][j][k][t][s][m]表示当前在
(
i
,
j
)
(i,j)
(i,j)位置,还剩下
k
k
k次技能可以使用,并且当前的攻击力是
m
m
m。剩下的
t
t
t表示在技能状态下,还剩下
t
t
t步可走,而
s
s
s就表示是否在技能状态。
然后我们算一算空间,完了,爆炸了。于是就选择睿智的观察数据范围,发现每次使用技能后能移动的步数很小。于是果断来DFS
void Dfs(int x,int y,int k,int g,int s,int ti)
{
x,y表示当前所在位置。k表示还剩下的技能次数,g表示当前攻击力。
s表示还剩下的步数,ti表示当前受到的伤害值。
if (x>n||y>m) return ;
攻击力+=Map[x][y]; 伤害值+=tmp*Map[x][y];伤害增加up!up!
if (x==n&&y==m) 讨论在技能状态下跑到终点的情况
if (s==1) 如果步数走到尽头
Dfs(x+1,y,k,g,s-1,ti);
Dfs(x,y+1,k,g,s-1,ti);
}
那么转移方程就只用考虑三个维度就可以啦
设定状态f
[
i
]
[
j
]
[
k
]
[i][j][k]
[i][j][k]表示当前位于
(
i
,
j
)
(i,j)
(i,j)位置,还剩下
k
k
k次技能可以使用。由于不好由过去转移过来,我们就想想往未来转移。
f[i+1][j][k]=min(f[i+1][j][k],f[i][j][k]);
f[i][j+1][k]=min(f[i][j+1][k],f[i][j][k]);
然后注意一下算伤害时的细节
写在DFS函数中的
f[x][y][k]=min(f[x][y][k],ti-cnt*Map[x][y]);
写在主函数循环中的
f[i][j][k]=f[i][j][k]+cnt*Map[i][j];
然后就做完啦