注:这是dp套路整理里面题的题解qwq
大家都知道,斐波那契数列是满足如下性质的一个数列:
F n = { 1 ( n ≤ 2 ) F n − 1 + F n − 2 ( n > 2 ) F_n=\left\{ \begin{array}{lr} 1\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (n\le 2) \\ F_{n-1}+F_{n-2}\ \ \ \ \ \ \ \ \ (n>2) \\ \end{array} \right. Fn={1 (n≤2)Fn−1+Fn−2 (n>2)
请你求出 F n m o d 1 0 9 + 7 ( 1 ≤ n < 2 63 ) F_n\ \rm mod\ 10^9+7(1\le n<2^{63}) Fn mod 109+7(1≤n<263)的值。
构造出矩阵快速幂转移矩阵:
[ F n F n − 1 ] = [ 1 1 1 0 ] ∗ [ F n − 1 F n − 2 ] \begin{bmatrix}F_{n}\\F_{n-1}\end{bmatrix}=\begin{bmatrix}1 & 1\\1 & 0\end{bmatrix}*\begin{bmatrix}F_{n-1}\\F_{n-2}\end{bmatrix} [FnFn−1]=[1110]∗[Fn−1Fn−2]
于是有 [ F n F n − 1 ] = [ 1 1 1 0 ] n − 2 ∗ [ F 2 F 1 ] = [ 1 1 1 0 ] n − 2 ∗ [ 1 1 ] \begin{bmatrix}F_{n}\\F_{n-1}\end{bmatrix}=\begin{bmatrix}1 & 1\\1 & 0\end{bmatrix}^{n-2}*\begin{bmatrix}F_{2}\\F_{1}\end{bmatrix}=\begin{bmatrix}1 & 1\\1 & 0\end{bmatrix}^{n-2}*\begin{bmatrix}1\\1\end{bmatrix} [FnFn−1]=[1110]n−2∗[F2F1]=[1110]n−2∗[11]
于是可以用矩阵快速幂求解 F n F_n Fn的值。
代码略
zyd要妥善安排他的后宫,他想在机房摆一群妹子,一共有 n n n个位置排成一排,每个位置可以摆妹子也可以不摆妹子。有些类型妹子如果摆在相邻的位置(隔着一个空的位置不算相邻),就不好看了。假定每种妹子数量无限,求摆妹子的方案数。
输入有 m + 1 m+1 m+1行,第一行有两个用空格隔开的正整数n、m,m表示妹子的种类数。接下来的 m m m行,每行有 m m m个0/1字符,第 i i i行第 j j j列为 a i j a_{ij} aij。若 a i j a_{ij} aij为1,则表示第 i i i种妹子第 j j j种妹子不能排在相邻的位置,输入保证对称。 n ≤ 1 0 9 , m ≤ 100 n\le 10^9,m\le 100 n≤109,m≤100
令 d p [ i ] [ j ] dp[i][j] dp[i][j]表示放了 i i i个妹子,最后一个妹子是 j j j的方案数。
d p [ i ] [ j ] = ∑ a j k = = 0 d p [ i − 1 ] [ k ] dp[i][j]=\sum_{a_{jk}==0} dp[i-1][k] dp[i][j]=∑ajk==0dp[i−1][k]
令 b i j = 1 − a i j b_{ij}=1-a_{ij} bij=1−aij
于是得到 [ d p [ i ] [ m ] d p [ i ] [ m − 1 ] d p [ i ] [ m − 2 ] . . . d p [ i ] [ 1 ] ] = [ b 11 b 12 b 13 . . . b 1 m b 21 b 22 b 23 . . . b 2 m b 31 b 32 b 33 . . . b 3 m . . . . . . . . . . . . . . . b m 1 b m 2 b m 3 . . . b m m ] ∗ [ d p [ i − 1 ] [ m ] d p [ i − 1 ] [ m − 1 ] d p [ i − 1 ] [ m − 2 ] . . . d p [ i − 1 ] [ 1 ] ] \begin{bmatrix}dp[i][m]\\dp[i][m-1]\\dp[i][m-2]\\...\\dp[i][1]\end{bmatrix}=\begin{bmatrix}b_{11} & b_{12} & b_{13} & ... & b_{1m}\\b_{21} & b_{22} &b_{23} & ... & b_{2m}\\b_{31} & b_{32} & b_{33} & ... & b_{3m}\\ ... & ... & ... & ... & ...\\ b_{m1} & b_{m2} & b_{m3} & ... & b_{mm}\end{bmatrix}*\begin{bmatrix}dp[i-1][m]\\dp[i-1][m-1]\\dp[i-1][m-2]\\...\\dp[i-1][1]\end{bmatrix} ⎣⎢⎢⎢⎢⎡dp[i][m]dp[i][m−1]dp[i][m−2]...dp[i][1]⎦⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎡b11b21b31...bm1b12b22b32...bm2b13b23b33...bm3...............b1mb2mb3m...bmm⎦⎥⎥⎥⎥⎤∗⎣⎢⎢⎢⎢⎡dp[i−1][m]dp[i−1][m−1]dp[i−1][m−2]...dp[i−1][1]⎦⎥⎥⎥⎥⎤
代码和剩下的步骤略。
给定一长度为 n n n的数列,请在不改变原数列顺序的前提下,从中随机的取出一定数量的整数,并使这些整数构成单调上升序列。 输出这类单调上升序列的最大长度。 n ≤ 1 0 5 n\le 10^5 n≤105。
f i = max j = 1 i − 1 { f j + 1 } , a j < a i f_i=\max_{j=1}^{i-1}\{f_j+1\},a_j
小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 W W W 的采集车,洞穴里总共有 n n n 种宝物,每种宝物的价值为 v i v_i vi,重量为 w i w_i wi,每种宝物有 m i m_i mi 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。 n ≤ 100 , ∑ m i ≤ 1 0 5 , 0 ≤ W ≤ 4 ∗ 1 0 4 n\le 100,\sum m_i\le 10^{5},0\le W\le 4*10^4 n≤100,∑mi≤105,0≤W≤4∗104。
令 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i种宝物,目前载重为 j j j的最大价值。
转移方程: d p [ i ] [ j ] = max { d p [ i − 1 ] [ j − k ∗ w i ] + k ∗ v i } dp[i][j]=\max\{dp[i-1][j-k*w_i]+k*v_i\} dp[i][j]=max{dp[i−1][j−k∗wi]+k∗vi}。
注意到对于每个 i i i, j j j和 j − k ∗ w i j-k*w_i j−k∗wi是模 w i w_i wi同余的。也就是说 j j j只会被与 j j j模 w i w_i wi相同的数影响。
设 j = p ∗ w i + r j=p*w_i+r j=p∗wi+r,则转移方程可化为 d p [ i ] [ j ] = max { d p [ i − 1 ] [ k ∗ w i + r ] + ( p − k ) ∗ v i } ( p − m i ≤ k ≤ p ) dp[i][j]=\max\{dp[i-1][k*w_i+r]+(p-k)*v_i\}(p-m_i \le k\le p) dp[i][j]=max{dp[i−1][k∗wi+r]+(p−k)∗vi}(p−mi≤k≤p)
拆开 p ∗ v i p*v_i p∗vi这项常数,得到 d p [ i ] [ j ] = max { d p [ i − 1 ] [ k ∗ w i + r ] − k ∗ v i } + p ∗ v i ( p − m i ≤ k ≤ p ) dp[i][j]=\max\{dp[i-1][k*w_i+r]-k*v_i\}+p*v_i(p-m_i\le k\le p) dp[i][j]=max{dp[i−1][k∗wi+r]−k∗vi}+p∗vi(p−mi≤k≤p)
发现这就是一个滑块窗口,所以用单调队列乱搞。
#include
#include
#include
#define re register int
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=100005;
int n,V,v[Size],w[Size],num[Size],dp[Size];
int hd,tl;
struct node {
int id,val;
} Queue[Size];
inline void push(int id,int val) {
while(hd<=tl && val>Queue[tl].val) tl--;
Queue[++tl].id=id;
Queue[tl].val=val;
}
inline void pop(int id,int num) {
while(hd<=tl && Queue[hd].id+num<id) hd++;
}
int main() {
n=read();
V=read();
int ans=0;
for(re i=1; i<=n; i++) {
v[i]=read();
w[i]=read();
num[i]=read();
if(!w[i]) {
n--;
ans+=v[i]*num[i];
}
}
for(re i=1; i<=n; i++) {
for(re r=0; r<w[i]; r++) {
int lim=(V-r)/w[i];
hd=1,tl=0;
for(re p=0; p<=lim; p++) {
push(p,dp[p*w[i]+r]-p*v[i]);
pop(p,num[i]);
dp[p*w[i]+r]=max(dp[p*w[i]+r],Queue[hd].val+p*v[i]);
}
}
}
printf("%d",dp[V]);
return 0;
}
马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街。商店街有 n n n个商店,并且它们之间的道路构成了一颗树的形状。
第 i i i个商店只卖第 i i i种物品,小苗对于这种物品的喜爱度是 w i w_i wi,物品的价格为 c i c_i ci,物品的库存是 d i d_i di。但是商店街有一项奇怪的规定:如果在商店 u , v u,v u,v买了东西,并且有一个商店 w w w在 u u u到 v v v的路径上,那么必须要在商店 w w w买东西。小葱身上有 m m m元钱,他想要尽量让小苗开心,所以他希望最大化小苗对买到物品的喜爱度之和。这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为OI选手的你,你能帮帮他吗? n ≤ 500 , m ≤ 4000 , w i ≤ 4000 , d i ≤ 100 n\le 500,m\le 4000,w_i\le 4000,d_i\le 100 n≤500,m≤4000,wi≤4000,di≤100
若在 u , v u,v u,v买了东西,那么 u , v u,v u,v路径之间的所有点都要买东西,也就是说买东西的节点构成了树上连通块。果断用点分治。
题目显然是一个多重背包,因此用dfs序转移+单调队列优化多充背包即可。
代码懒得写……
给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大,并输出最大的总价值。数据范围: N ⩽ 100 , W ⩽ 2 30 N⩽100,W⩽2^{30} N⩽100,W⩽230。每颗宝石的重量可以表示为 a ∗ 2 b ( a ⩽ 10 ; b ⩽ 30 ) a*2^b(a\leqslant 10;b\leqslant 30) a∗2b(a⩽10;b⩽30)。
直接背包显然容量太大了,注意到保证每颗宝石的重量可以表示为 a ∗ 2 b ( a ≤ 10 , b ≤ 30 ) a*2^b(a\le10,b\le30) a∗2b(a≤10,b≤30)的形式,考虑把 b b b相同的宝石分为一组,进行分组背包。
假设第 i i i组宝石的重量都可以表示为 a ∗ 2 i a*2^i a∗2i, d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i组宝石,且所用重量不超过 j ∗ 2 i j*2^{i} j∗2i得到的最大价值。
先做一次dp预处理出组内子状态信息。然后组见合并的dp方程: d p [ i ] [ j ] = max { d p [ i ] [ j − k ] + d p [ i − 1 ] [ m i n ( s u m [ i − 1 ] , 2 k + ( W 2 i − 1 & 1 ) ) ] } dp[i][j]=\max\{dp[i][j-k]+dp[i-1][min(sum[i-1],2k+(\large\frac{W}{2^{i-1}}\normalsize \&1))]\} dp[i][j]=max{dp[i][j−k]+dp[i−1][min(sum[i−1],2k+(2i−1W&1))]}。
后面这一串的意义:第 i i i组用了 ( j − k ) ∗ 2 i (j-k)*2^i (j−k)∗2i的重量,则第 i − 1 i-1 i−1组用了 k ∗ 2 i = 2 k ∗ 2 i − 1 k*2^i=2k*2^{i-1} k∗2i=2k∗2i−1的重量。但如果只算 d p [ i − 1 ] [ 2 k ] dp[i-1][2k] dp[i−1][2k],最后的总重量实际上是 2 ⌊ l o g 2 W ⌋ 2^{\lfloor log_2W\rfloor} 2⌊log2W⌋的,所以应该要在中间加上 W W W后面二进制位的重量。而 W 2 i − 1 & 1 \large\frac{W}{2^{i-1}}\normalsize \&1 2i−1W&1表示的是 W W W的第 i − 1 i-1 i−1位的值。加上这一位转移,最后的答案就是 d p [ l o g 2 W ] [ 1 ] dp[log_2W][1] dp[log2W][1]。
#include
#include
#include
#include
#include
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int Size=105;
const int Maxb=35;
int n,W,w[Size],v[Size],sum[Size];
vector<int> a[Maxb];
int dp[Maxb][Size];
int main() {
while(scanf("%d%d",&n,&W)==2 && n!=-1) {
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
for(re i=0; i<=31; i++) a[i].clear();
int maxb=0;
for(re i=1; i<=n; i++) {
w[i]=read();
v[i]=read();
int b=0;
while(!(w[i]&1)) {
b++;
w[i]>>=1;
}
a[b].push_back(i);
if(b>maxb) maxb=b;
sum[b]+=w[i];
}
for(re i=0; i<=maxb; i++) {
int len=a[i].size();
for(re j=0; j<len; j++) {
for(re k=sum[i]; k>=w[a[i][j]]; k--) {
dp[i][k]=max(dp[i][k],dp[i][k-w[a[i][j]]]+v[a[i][j]]);
}
}
}
int logW=log2(W);
for(re i=1; i<=logW; i++) {
sum[i]+=(sum[i-1]+1)>>1;
for(re j=sum[i]; j>=0; j--) {
for(re k=0; k<=j; k++) {
dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[i-1][min(sum[i-1],(k<<1)|(W>>(i-1)&1))]);
}
}
}
printf("%d\n",dp[logW][1]);
}
return 0;
}
显然是一个区间dp,最小得分方程为 d p [ i ] [ j ] = min { d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + s u m ( i , j ) } dp[i][j]=\min\{dp[i][k]+dp[k+1][j]+sum(i,j)\} dp[i][j]=min{dp[i][k]+dp[k+1][j]+sum(i,j)}。满足四边形不等式,断环为链后用四边形不等式优化。
最大得分无法用四边形不等式优化,但是 [ l , r ] [l,r] [l,r]最大得分只能在 k = l k=l k=l或 k + 1 = r k+1=r k+1=r时取到。
#include
#include
#include
#include
#include
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int Size=205;
const int INF=0x3f3f3f3f;
int n,a[Size],sum[Size];
int dpmax[Size][Size],dpmin[Size][Size],s[Size][Size];
int main() {
memset(dpmin,0x3f,sizeof(dpmin));
n=read();
for(re i=1; i<=n; i++) a[i]=a[i+n]=read();
for(re i=1; i<=(n<<1); i++) {
sum[i]=sum[i-1]+a[i];
dpmax[i][i]=dpmin[i][i]=0;
s[i][i]=i;
}
for(re len=2; len<=n; len++) {
const int maxl=(n<<1)-len+1;
for(re l=1; l<=maxl; l++) {
const int r=l+len-1,val=sum[r]-sum[l-1];
dpmax[l][r]=max(dpmax[l][r-1],dpmax[l+1][r])+val;
int minn=INF,id=0;
for(re k=s[l][r-1]; k<=s[l+1][r]; k++) {
if(dpmin[l][k]+dpmin[k+1][r]+val<minn) {
minn=dpmin[l][k]+dpmin[k+1][r]+val;
id=k;
}
}
dpmin[l][r]=minn;
s[l][r]=id;
}
}
int ansmax=0,ansmin=INF;
for(re i=1; i<=n; i++) {
ansmax=max(ansmax,dpmax[i][i+n-1]);
ansmin=min(ansmin,dpmin[i][i+n-1]);
}
printf("%d\n%d",ansmin,ansmax);
return 0;
}
水题,记忆化bfs就可以了。
另外吐槽一下,洛咕上面题解都写丑了,每次按按钮不用 O ( n ) O(n) O(n)更新,用小技巧可以做到 O ( 1 ) O(1) O(1)。
#include
#include
#include
#include
#include
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int Size=105;
const int Maxn=2055;
const int INF=0x3f3f3f3f;
int n,m,a[Size],b[Size],Queue[Maxn],dis[Maxn];
bool vis[Maxn];
int bfs() {
int hd=0,tl=0;
vis[Queue[++tl]=(1<<n)-1]=true;
while(hd<tl) {
int x=Queue[++hd];
for(re i=1; i<=m; i++) {
int val=x&a[i]|b[i];
if(!vis[val]) {
vis[val]=true;
Queue[++tl]=val;
dis[val]=dis[x]+1;
if(!val) {
return dis[val];
}
}
}
}
return -1;
}
int main() {
n=read();
m=read();
for(re i=1; i<=m; i++) {
for(re j=1; j<=n; j++) {
int val=read();
if(val!=1) a[i]^=1<<(j-1);
if(val==-1) b[i]^=1<<(j-1);
}
}
printf("%d",bfs());
return 0;
}
也是水题……