dp
目录
- P1020 [NOIP1999 普及组] 导弹拦截(LIS)
- P1439 【模板】最长公共子序列:
- P1352 没有上司的舞会(树状dp):
- P1880 [NOI1995] 石子合并(区间dp):
- P2602 [ZJOI2010] 数字计数(数位dp)
- P1896 [SCOI2005]互不侵犯(状压dp)
- 斜率优化dp P3195 [HNOI2008]玩具装箱
- dp套dp P4158 [SCOI2009]粉刷匠
- 动态dp P4719 【模板】"动态 DP"&动态树分治
- 换根dp P3478 [POI2008] STA-Station
- 基环树dp P2607 [ZJOI2008] 骑士
- 期望dp P4316 绿豆蛙的归宿
- 期望dp2 P4550 收集邮票
- 博弈dp P2734 [USACO3.3]游戏 A Game
- 博弈dp2 P2964 [USACO09NOV]A Coin Game S
- 博弈dp3 sg函数类 P1857 质数取石子
- 概率dp P1850 [NOIP2016 提高组] 换教室
P1020 [NOIP1999 普及组] 导弹拦截(LIS)
LIS:最长单调上升子序列
lis 似乎没有裸的原题,这道题相当于求一个最长单调下降子序列和一个最长单调不降子序列,而且必须要 的做法才能过
(求需要多少套系统相当于此时使用了 套系统,在拦截第 枚导弹时应该贪心的,在能够拦截的情况下使用这些系统里最后拦截的导弹最小的系统来拦截,如果没有能拦截的系统,就需要在加一套系统,因此求最长单调不降子序列相当于在求有多少次出现需要加一套系统的情况,也就是需要多少套系统)
LIS 做法:
if(a[i]>a[j]&&1<=j<i)
dp[i]=max(dp[i],dp[j]+1)
这里的 dp[i]
指以 a[i]
为结尾的最长的 LIS 的长度。
同时需要初始化 dp[i]=1(1<=i<=n)
LIS 做法:
从另一种角度来考虑,dp[i]
变为代表长度为 的 LIS 的最后一个元素,然后枚举,此时我们希望最后一个元素应该是越小越好,所以枚举时如果发现无法直接接在后面,就可以寻找 dp[1]
到dp[len-1]
的第一个大于等于这个元素的lis,并将它替换成枚举的元素。由于 dp 这个序列其实是单调递增的,所以可以用二分来加速寻找,所以时间复杂度才得以降低
#include<bits/stdc++.h>
using namespace std;
int a[100005],n,d1[1000005],d2[1000005],len;
int main()
{
while(cin>>a[++n]);
n--;
d1[++len]=a[1];
for(int i = 2;i<=n;i++)
{
if(a[i]<=d1[len]) d1[++len]=a[i];
else{
int p=upper_bound(d1+1,d1+len+1,a[i],greater<int>())-d1;
d1[p]=a[i];
}
}
cout<<len<<endl;
len=0;
d2[++len]=a[1];
for(int i = 2;i<=n;i++)
{
if(a[i]>d2[len]) d2[++len]=a[i];
else{
int p=lower_bound(d2+1,d2+len+1,a[i])-d2;
d2[p]=a[i];
}
}
cout<<len;
return 0;
}
P1439 【模板】最长公共子序列:
推 lcs 的标准方法其实是 的:
i 从 1 到 n:
j 从 1 到 n:
如果 a[i]==a[j]:
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1)
否则:
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
dp[i][j]指的是第一个序列的前 个元素与第二个序列的前 个元素的最长的 lcs 的长度,如果此时第 个元素与第 个元素一样的话,则可以从 dp[i-1][j-1]
推过来.
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i <= b;i ++)
#define ll long long
using namespace std;
const int maxn = 1e6+4;
int n = 1, mp[maxn], dp[maxn], len, a[maxn],b;
int r[maxn];
int main()
{
int n;
cin>>n;
for1(i,1,n)
cin>>a[i],mp[a[i]] = i;
for1(i,1,n)
cin>>b,r[i] = mp[b];
dp[++len] = r[1];
for1(i,2,n)
if(r[i] >= dp[len])
dp[++ len] = r[i];
else
{
int p = lower_bound(dp + 1, dp + len + 1, r[i]) - dp;
dp[p] = r[i];
}
cout<<len<<endl;
return 0;
}
P1352 没有上司的舞会(树状dp):
#include<bits/stdc++.h>
using namespace std;
int happy[1000000],n,dp[1000005][2],root;
bool vis[100005];
vector<int> a[100005];
void dfs(int x)
{
dp[x][1]=happy[x];
if(a[x].size()==0) return ;
for(int i = 0;i <a[x].size();i++)
{
int y = a[x][i];
dfs(y);
dp[x][0]+=max(dp[y][0],dp[y][1]);
dp[x][1]+=dp[y][0];
}
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <=n;i++)
scanf("%d",&happy[i]);
int x,y;
for(int i = 1;i <=n-1;i++)
{
scanf("%d%d",&x,&y);
a[y].push_back(x);
vis[x]=1;
}
for(int i = 1;i <=n;i++) if(vis[i]==0) {root=i;break;}
dfs(root);
printf("%d",max(dp[root][0],dp[root][1]));
return 0;
}
P1880 [NOI1995] 石子合并(区间dp):
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i <= b;i ++)
#define ll long long
using namespace std;
const int maxn = 1e4+4;
const int inf = 1e9+7;
int a[maxn], dp1[maxn][maxn], dp2[maxn][maxn];
int n,vis[maxn][maxn];
void dfs(int l, int r)
{
vis[l][r] = 1;
if(l == r)
{
dp1[l][r] = 0;
dp2[l][r] = 0;
return ;
}
dp1[l][r] = inf;
for1(k,l,r - 1)
{
if(vis[l][k] == 0)
dfs(l, k);
if(vis[k + 1][r] == 0)
dfs(k + 1, r);
dp1[l][r] = min(dp1[l][r], dp1[l][k] + dp1[k + 1][r] + a[r] - a[l-1]);
dp2[l][r] = max(dp2[l][r], dp2[l][k] + dp2[k + 1][r] + a[r] - a[l-1]);
}
}
int main()
{
cin>>n;
for1(i,1,n)
cin>>a[i],a[n + i] = a[i];
for1(i,1,n * 2)
a[i] += a[i - 1];
dfs(1,n * 2);
int mi = inf , mx = -inf;
for1(i,1,n)
{
mi = min(mi, dp1[i][i + n - 1]);
mx = max(mx, dp2[i][i + n - 1]);
}
cout<<mi<<endl;
cout<<mx;
return 0;
}
P2602 [ZJOI2010] 数字计数(数位dp)
#include<bits/stdc++.h>
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
#define ll long long
using namespace std;
const ll maxn = 50;
const ll inf = 1e9+7;
ll a,b, dp[maxn][3][maxn][3];
ll num[maxn];
ll dfs(ll now, ll sj, ll sum, ll zero, ll d)
{
if(now == 0) return sum;
if(dp[now][sj][sum][zero] != -1) return dp[now][sj][sum][zero];
ll ans = 0;
for1(i,0,9)
{
if(! sj && i > num[now]) break;
//如果顶着上界,并且枚举的数码已经超了,那再枚举就不合法了
ans += dfs( now - 1,//下一位
sj || (i < num[now]),//之前就顶着上界并且这次也是正好枚举到上界才为1
sum + ( (!zero || i) && (i == d))//没有前导0并且这一位正好枚举的是d才行
, zero && (i == 0) //之前一直有并且这次枚举的也是0才行
,d);
}
dp[now][sj][sum][zero] = ans;
return ans;
}
ll cl(ll x,ll d)
{
ll len = 0;
while(x)
{
num[++ len] = x % 10;
x /= 10;
}
memset(dp, -1, sizeof(dp));
return dfs(len, 0, 0, 1, d);
//从第几位开始
//是否和枚举在上界 碰到就是1
//计算的数量
//是否有前导0 现在是第len位,相当于有前导0
//计算的数码
}
int main()
{
cin>>a>>b;
for1(i,0,9)
cout<<cl(b,i) - cl(a - 1,i)<<' ';
return 0;
}
P1896 [SCOI2005]互不侵犯(状压dp)
#include<bits/stdc++.h>
#define for1(i,a,b) for(register int i = a;i<=b;i++)
#define ll long long
using namespace std;
ll n,m,k,num[10000],zt[10000],cnt,f[20][1000][200],ans;
//f[i[j][l] 表示第i行状态为j,且前i行有l的的方案数
void cl()
{
cnt=0;
for1(i,0,(1<<n)-1)
{
if(i&(i<<1))continue;//若有相邻的1表示不合法
int sum=0;
for1(j,0,n-1)
if(i&(1<<j))++sum;//记录有多少个1
zt[++cnt]=i;
num[cnt]=sum;
}return;
}
int main()
{
f[0][1][0]=1;
cin>>n>>k;
cl();
f[0][1][0]=1;
for1(i,1,n)
for1(j,1,cnt)
for1(l,0,k)//第i行的合法情况
if(l>=num[j])
for1(t,1,cnt)// 第i行的合法时前i-1行可以继承的状态
if(!(zt[t]&zt[j])//上下相邻
&&!(zt[t]&(zt[j]<<1))//第i行与第i-1行左上相邻
//0100
//0010
&&!(zt[t]&(zt[j]>>1)))//第i行与第i-1行右上相邻
//0010 i-1
//0100 i
f[i][j][l]+=f[i-1][t][l-num[j]];
for1(i,1,cnt)
ans+=f[n][i][k];
printf("%lld\n",ans);
return 0;
}
斜率优化dp P3195 [HNOI2008]玩具装箱
#include<bits/stdc++.h>
#define ll long long
#define for1(i,a,b) for(register int i = a;i <= b;i ++)
const int N = 500005;
int n, L;
struct node{
double sum[N], dp[N];
int dl[N], hd, tl;
node ()
{
hd = tl = 1;
for1(i,1,n)
dp[i] = 0;
}
void Read (const int &x)
{
std::cin >> sum[x];
return ;
}
void QianZuiHe ()
{
for1(i,1,n)
sum[i] += sum[i - 1];
return ;
}
double a (const int &x)
{
return sum[x] + x;
}
double b (const int &x)
{
return a (x) + L + 1;
}
double X (const int &x)
{
return b (x);
}
double Y (const int &x)
{
return dp[x] + b (x) * b (x);
}
double XieLv (const int &x, const int &y)
{
return (Y(x) - Y(y)) / (X (x) - X (y));
}
void CaoZuo ()
{
QianZuiHe();
for1(i,1,n)
{
while (hd < tl && XieLv (dl[hd], dl[hd+1]) < 2 * a (i))
hd ++;
dp [i] = dp[dl[hd]] + (a (i) - b (dl[hd])) * (a (i) - b (dl[hd]) );
while (hd < tl && XieLv(i, dl[tl-1]) < XieLv(dl[tl-1],dl[tl]))
tl --;
dl [++ tl] = i;
}
}
void Write ()
{
std::cout<< (ll) dp[n] <<'\n';
}
}a;
int main()
{
std::cin>> n >> L;
for1(i,1,n) a.Read (i);
a.CaoZuo ();
a.Write ();
return 0;
}
dp套dp P4158 [SCOI2009]粉刷匠
#include<bits/stdc++.h>
#define for1(i,a,b) for (int i = a;i <= b;i ++)
#define ll long long
const int N = 5005;
int dp[N][N][3], color[N], ans[N], n, m, k;
std::string s;
void cl()
{
for1(i,1,m)
for1(j,1,k)
dp[i][j][0]=0,dp[i][j][1]=0;
//清空
for1(i,1,m)//第i格
{
for1(j,1,i)//刷j次
{
dp[i][j][color[i]] = dp[i - 1][j][color[i]] + 1;//白嫖
dp[i][j][! color[i]] = dp[i - 1][j][! color[i]];
dp[i][j][color[i]] = std::max(dp[i][j][color[i]], std::max(dp[i-1][j-1][0],dp[i-1][j-1][1]) + 1);
dp[i][j][! color[i]] = std::max(dp[i][j][!color[i]], std::max(dp[i-1][j-1][0],dp[i-1][j-1][1]));
//刷一次新的
}
}
for(int i=k;i>=1;i--)//背包
for1(j,0,std::min(m,i))
ans[i] = std::max(ans[i], ans[i - j] + std::max(dp[m][j][0],dp[m][j][1]));
}
void ShuRu()
{
std::cin>> n >> m >> k;
for1(l,1,n)
{
std::cin >> s;
for1(j,0,s.size()-1)
{
if (s[j] == '1')
{
color[j + 1] = 1;
}
else
color[j + 1] = 0;
}
cl();
}
}
void ShuChu()
{
std::cout<< ans[k];
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
ShuRu();
ShuChu();
return 0;
}
动态dp P4719 【模板】"动态 DP"&动态树分治
(并没有完全掌握。考场上打不出来。。。)
#include <bits/stdc++.h>
#define for1(i,a,b) for (int i = a;i <= b;i ++)
const int MaxN = 400005;
const int INF = 99824353;
struct Matrix
{
int matrix[2][2];
Matrix()
{
for1(i,0,1)
for1(j,0,1)
matrix[i][j] = 0;
}
};
Matrix operator * (Matrix a, Matrix b)
{
Matrix c;
for1(i,0,1)
for1(j,0,1)
for1(k,0,1)
c.matrix[i][j] = std::max(c.matrix[i][j], a.matrix[i][k] + b.matrix[k][j]);
return c;
}
int n, m;
int cntv, cnte;
int a[MaxN];
int F[MaxN][2];
Matrix Val[MaxN];
int Hd[MaxN], To[MaxN], Nex[MaxN];
int Top[MaxN], Id[MaxN], Dfn[MaxN], End[MaxN];
int Fa[MaxN], Siz[MaxN], Dep[MaxN], ZhongSon[MaxN];
struct SegmentTree
{
int L[MaxN], R[MaxN];
Matrix ma[MaxN];
void BuildTree(int left, int right, int i)
{
L[i] = left;
R[i] = right;
if (L[i] == R[i])
{
ma[i] = Val[Dfn[L[i]]];
return;
}
int mid = (L[i] + R[i]) >> 1;
BuildTree(L[i], mid, i * 2);
BuildTree(mid + 1, R[i], i * 2 + 1);
ma[i] = ma[i * 2] * ma[i * 2 + 1];
}
void UpdateTree(int x, int i)
{
if (L[i] == R[i])
{
ma[i] = Val[Dfn[x]];
return;
}
int mid = (L[i] + R[i]) >> 1;
if (x <= mid)
UpdateTree(x, i * 2);
else
UpdateTree(x, i * 2 + 1);
ma[i] = ma[i * 2] * ma[i * 2 + 1];
}
Matrix QueryTree(int left, int right, int i)
{
if (L[i] == left && R[i] == right)
return ma[i];
int mid = (L[i] + R[i]) >> 1;
if (right <= mid)
return QueryTree(left, right, i * 2);
else if (left > mid)
return QueryTree(left, right, i * 2 + 1);
else
return QueryTree(left, mid, i * 2) * QueryTree(mid + 1, right, i * 2 + 1);
}
} Tree;
void add(int x, int y)
{
cnte++; To[cnte] = y;
Nex[cnte] = Hd[x];
Hd[x] = cnte;
}
void read()
{
std::cin >> n >> m;
for1(i,1,n)
std::cin>> a[i];
int x, y;
for1(i,1,n-1)
{
std::cin>> x >> y;
add(x, y);
add(y, x);
}
}
void dfs1(int u)
{
Siz[u] = 1;
for (int i = Hd[u]; i; i = Nex[i])
{
int v = To[i];
if (v == Fa[u]) continue;
Fa[v] = u; Dep[v] = Dep[u] + 1;
dfs1(v);
Siz[u] += Siz[v];
if (Siz[v] > Siz[ZhongSon[u]]) ZhongSon[u] = v;
}
}
void dfs2(int u, int chain)
{
cntv++;
Id[u] = cntv;
Dfn[cntv] = u;
Top[u] = chain;
End[chain] = std::max(End[chain], cntv);
F[u][0] = 0, F[u][1] = a[u];
Val[u].matrix[0][0] = Val[u].matrix[0][1] = 0;
Val[u].matrix[1][0] = a[u];
if (ZhongSon[u] != 0)
{
dfs2(ZhongSon[u], chain);
F[u][0] += std::max(F[ZhongSon[u]][0], F[ZhongSon[u]][1]);
F[u][1] += F[ZhongSon[u]][0];
}
for (int i = Hd[u]; i; i = Nex[i])
{
int v = To[i];
if (v == Fa[u] || v == ZhongSon[u]) continue;
dfs2(v, v);
F[u][0] += std::max(F[v][0], F[v][1]);
F[u][1] += F[v][0];
Val[u].matrix[0][0] +=std:: max(F[v][0], F[v][1]);
Val[u].matrix[0][1] = Val[u].matrix[0][0];
Val[u].matrix[1][0] += F[v][0];
}
}
void UpdatePath (int u, int w)
{
Val[u].matrix[1][0] += w - a[u];
a[u] = w;
Matrix before, after;
while (u != 0)
{
before = Tree.QueryTree(Id[Top[u]], End[Top[u]], 1);
Tree.UpdateTree(Id[u], 1);
after = Tree.QueryTree(Id[Top[u]], End[Top[u]], 1);
u = Fa[Top[u]];
Val[u].matrix[0][0] += std::max(after.matrix[0][0], after.matrix[1][0])
- std::max(before.matrix[0][0], before.matrix[1][0]);
Val[u].matrix[0][1] = Val[u].matrix[0][0];
Val[u].matrix[1][0] += after.matrix[0][0] - before.matrix[0][0];
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
read();
dfs1(1);
dfs2(1, 1);
Tree.BuildTree(1, n, 1);
int x, y;
for1(i,1,m)
{
std::cin>> x>> y;
UpdatePath(x, y);
Matrix ans = Tree.QueryTree(Id[1], End[1], 1);
printf("%d\n", std::max(ans.matrix[0][0], ans.matrix[1][0]));
}
return 0;
}
换根dp P3478 [POI2008] STA-Station
#include <bits/stdc++.h>
#define for1(i,a,b) for (int i = a;i <= b;i ++)
#define ll long long
const int maxn=1e6+5;
struct node{
int to;
int nex;
}a[maxn * 2];
int n,cnt,id;
int hd[maxn];
ll ans;
ll f[maxn],dep[maxn],siz[maxn];
void ru(int x, int y)
{
a[++cnt].to = y;
a[cnt].nex = hd[x];
hd[x] = cnt;
}
void dfs1(int x,int fa)
{
siz[x] = 1;
dep[x] = dep[fa] + 1;
for(int i = hd[x];i;i = a[i].nex)
{
int v = a[i].to;
if(v == fa) continue;
dfs1(v, x);
siz[x] += siz[v];
}
}
void dfs2(int x,int fa)
{
for(int i = hd[x];i;i = a[i].nex)
{
int v = a[i].to;
if(v == fa) continue;
f[v] = f[x] + n - 2 * siz[v];
dfs2(v,x);
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
int x,y;
for1(i,1,n-1)
{
std:: cin >> x >> y;
ru(x,y);
ru(y,x);
}
dfs1(1,0);
for1(i,1,n)
f[1] += dep[i];
int ji;
dfs2(1,0);
for1(i,1,n)
if(ans<f[i])
{
ans = f[i];
ji = i;
}
printf("%d",ji);
return 0;
}
基环树dp P2607 [ZJOI2008] 骑士
#include <bits/stdc++.h>
#define for1(i,a,b) for (int i = a;i <= b;i ++)
#define ll long long
const int maxn = 1e6+5;
struct node{
int nex;
int to;
}a[maxn+5];
int fa[maxn];
int hd[maxn],cnt;
ll n,w[maxn];
ll f[maxn][3];
bool vis[maxn];
ll ans;
int root;
void ru(int x,int y)
{
a[++cnt].to=y;
a[cnt].nex=hd[x];
hd[x]=cnt;
}
void dp(int now)
{
vis[now]=1;
f[now][0]=0;
f[now][1]=w[now];
for (int i=hd[now];i;i=a[i].nex)
{
int v = a[i].to;
if(v != root)
{
dp(v);
f[now][0] += std::max(f[v][1], f[v][0]);
f[now][1] += f[v][0];
}
else//强制不选自己
f[v][1] = -(ll)(1e18);
}
}
void cl(int x)
{
vis[x] = 1;
root = x;
while(!vis[fa[root]])//找环
{
root = fa[root];
vis[root] = 1;
}
dp(root);//不选自己对他dp
ll t= f[root][0];
vis[root] = 1;
root = fa[root];
dp(root);//强制不选他的父亲,对他的父亲dp
ans += std::max(t, f[root][0]);
return;
}
int main()
{
// std::ios::sync_with_stdio(false);
// std::cin.tie(nullptr);
std:: cin >> n;
int y;
for1(i,1,n)
{
std::cin>>w[i] >> y;
fa[i] = y;
ru(y,i);
}
for1(i,1,n)
{
if(vis[i] == 0)
{
cl(i);
}
}
std::cout<< ans<<'\n';
}
期望dp P4316 绿豆蛙的归宿
#include <bits/stdc++.h>
#define for1(i,a,b) for (int i = a;i <= b;i ++)
#define ll long long
const int maxn = 1e6+5;
const int inf = 99824353;
struct node{
int nex;
int to;
double w;
}a[maxn*2];
int n,m;
int hd[maxn],cnt;
double f[maxn];
double du[maxn];
int vis[maxn];
void ru(int x,int y,int z)
{
a[++cnt].to = y;
a[cnt].w = z;
a[cnt].nex = hd[x];
hd[x] = cnt;
}
void dfs(int x)
{
if(vis[x]) return;
vis[x] = 1;
for(int i=hd[x];i;i=a[i].nex)
{
int v = a[i].to;
dfs(v);
f[x] += (a[i].w + f[v]) * (1.0 / du[x] * 1.0);
}
return ;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin>>n>>m;
int x,y,z;
for1(i,1,m)
{
std::cin>>x>>y>>z;
ru(x,y,z);
du[x] ++;
}
f[n] = 0;
dfs(1);
printf("%.2f",f[1]);
return 0;
}
期望dp2 P4550 收集邮票
#include<bits/stdc++.h>
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
#define ll long long
const ll maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
double f[maxn],g[maxn],n;//f[i]指抽到了i种卡,之后抽完的花费
//g[i]指的是天上掉下来i种卡(相当于此时抽卡的花费是1),之后集齐所有卡的价钱
int main()
{
std::cin>>n;
f[(int)(n)] = 0;
g[(int)(n)] = 0;
for(int i=n-1;i>=0;i--)
{
double ji1 = (double)(i)/n;
double ji2 = (n-(double)(i))/n;
f[i] = (ji2 * f[i + 1] + 1)
/ (1 - ji1);
g[i] = (g[i + 1] * ji2 + f[i] * ji1 + f[i + 1] * ji2 + 1)
/ (1 - ji1);
// f[i]= (f[i] + 1) * (i/n) + (f[i+1]+1) * ((n-i)/n);
// g[i]= (g[i]之后的花费 +f[i]之后每次上涨1块 + 1这次花1块) * (i/n) + (g[i+1]//之后的花费 + f[i+1]//每次多花一块 + 1本次花一块) * ((n-i)/n);
}
printf("%.2lf",g[0]);
return 0;
}
博弈dp可以理解为,自己能得到的最大值 = 取的值 + 区间和- 对手取得的最大值。
由于这里的自己是宽泛的,所以对于对手而言也可以理解为自己能取得的最大值,是一个非常妙的换定义的思想.
博弈dp P2734 [USACO3.3]游戏 A Game
#include<bits/stdc++.h>
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
#define ll long long
const ll maxn = 1e3 + 10;
const ll mod = 100003;
int n,a[maxn],f[maxn][maxn],sum[maxn];
int main()
{
std::cin>> n;
for1(i,1,n)
{
std::cin>>a[i];
sum[i] = sum[i - 1] + a[i];
f[i][i] = a[i];
}
for (int i = n - 1;i >= 1;i --)
for1(j,i+1,n)
f[i][j] = std::max((sum[j] - sum[i - 1]) - f[i + 1][j],
(sum[j] - sum[i - 1]) - f[i][j - 1]);//即对方在l到r-1或r+1到r这个 区间内做出最优决策,那么第一个人就只能取对手没取的
std::cout<< f[1][n] << ' ' << sum[n] - f[1][n]<<'\n';
return 0;
}
博弈dp2 P2964 [USACO09NOV]A Coin Game S
#include<bits/stdc++.h>
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
#define ll long long
const ll maxn = 2e3 + 10;
const ll mod = 100003;
int n,a[maxn],f[maxn][maxn],sum[maxn];
int main()
{
std::cin>> n;
for(int i = n;i >= 1;i --)
std::cin>> a[i];
for1(i,1,n)
sum[i] = sum[i-1] + a[i];
for1(i,1,n)
for1(j,1,n)
{
f[i][j] = f[i][j - 1];//这个优化确实自己想不到,看题解的qwq
int k = 2 * j - 1;
if(k <= i)
f[i][j] = std::max(f[i][j],sum[i] - f[i - k][k]);
k+=1;
if(k<=i)
f[i][j] = std::max(f[i][j],sum[i] - f[i - k][k]);
}
//朴素的方程 f[i][j]=s[n]-s[1] - max{f[i+j][k]} (1≤k≤min(2j,n-i-j+1))
std::cout<< f[n][1]<<'\n';
return 0;
}
博弈dp3 sg函数类 P1857 质数取石子
需要注意的是
- 初始化sg函数
- 必胜点只能由必败点更新过来
#include<bits/stdc++.h>
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
#define ll long long
const int maxn = 2e4 + 10;
const int inf = 998244353;
int f[maxn],zs[maxn],cnt,T,n;
bool vis[maxn];//0必败 1必胜
void init()
{
for1(i,2,maxn-5)//线筛
{
if (vis[i] == 0)
zs[++cnt]=i;
for (int j = 1;j <= cnt && i * zs[j] < maxn;j ++)
{
vis[i*zs[j]] = 1;
if (i % zs[j] == 0) break;
}
}
for1(i,2,maxn-5)//初始化
{
bool flg = false;
for (int j = 1;j <= cnt && i - zs[j] >= 0;j ++)
//只要有一个儿子会引向必败点,那这个点就是必胜点
flg|=1-vis[i-zs[j]];
vis[i]=flg;
}
for1(i,2,maxn-5)
{
int ans;
if(vis[i])
ans = inf;
else
ans = -inf;
for (int j = 1;j <= cnt && i - zs[j] >= 0;j ++)
if (vis[i] == 1)//必胜点尽量快
{
if(vis[i-zs[j]] == 0)//必胜点只能去到必败点
ans = std::min(ans,f[i - zs[j]]);
}
else//比柏点尽量拖
ans = std::max(ans,f[i - zs[j]]);
f[i] = ans + 1;
}
}
int main()
{
init();
std::cin >> T;
while (T--)
{
std::cin>> n;
if (vis[n] == 0)
std::cout<<-1 <<'\n';
else
std::cout<<f[n] <<'\n';
}
return 0;
}
概率dp P1850 [NOIP2016 提高组] 换教室
#include <bits/stdc++.h>
#define for1(i,a,b) for(register int i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn = 4000 + 5;
const double inf = 1e17 + 5;
int n, m, v, e;
ll c[maxn][5], mp[505][505];
double k[maxn], dp[maxn][maxn][4], ans;
inline ll read() {
char ch = getchar(); int u = 0, f = 1;
while (!isdigit(ch)) {if (ch == '-')f = -1; ch = getchar();}
while (isdigit(ch)) {u = u * 10 + ch - 48; ch = getchar();}return u * f;
}
void in()
{
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
n = read();
m = read();
v = read();
e = read();
for1(i,1,v)
for1(j,1,v)
mp[i][j] = inf;
for1(i,1,n)
c[i][0] = read();
for1(i,1,n)
c[i][1] = read();
for1(i,1,n)
scanf("%lf",&k[i]);
ll x, y, z;
for1(i,1,e)
{
x = read();
y = read();
z = read();
mp[x][y] = min(mp[x][y], z);
mp[y][x] = mp[x][y];
}
}
void floyd()
{
for1(i,1,v)
mp[i][i] = 0;
for1(k,1,v)
for1(i,1,v)
for1(j,1,v)
mp[j][i] = mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
void cl()
{
for1(i,0,n)
for1(j,0,m)
dp[i][j][0] = inf,
dp[i][j][1] = inf;
dp[1][0][0] = 0;
dp[1][1][1] = 0;
for1(i,2,n)
{
dp[i][0][0] = dp[i - 1][0][0] + mp[c[i - 1][0]][c[i][0]];
for1(j,1,min(i, m))
{
int ji1 = c[i - 1][0], ji2 = c[i - 1][1], ji3 = c[i][0], ji4 = c[i][1];
//ji1 是上一次换之前所在的位置,ji2 是上一次换之后所在的位置
//ji3 是这一次换之前所在的位置,ji4 是这一次换之后所在的位置
dp[i][j][0] = min(dp[i][j][0],
min(dp[i - 1][j][0] + mp[ji1][ji3], //上一次没换
dp[i - 1][j][1] + mp[ji1][ji3] * (1 - k[i - 1]) + mp[ji2][ji3] * k[i - 1]));//上一次换了,有k的概率换不成
dp[i][j][1] = min(dp[i][j][1],
min(dp[i - 1][j - 1][0] + mp[ji1][ji3] * (1 - k[i]) //上一次没换,但是这一次换了
+ mp[ji1][ji4] * k[i], dp[i - 1][j - 1][1] + mp[ji2][ji4] * k[i] * k[i - 1] + mp[ji2][ji3] * k[i - 1] * (1 - k[i]) + mp[ji1][ji4] * (1 - k[i - 1]) * k[i] + mp[ji1][ji3] * (1 - k[i - 1]) * (1 - k[i])));
//上一次换了,这一次也换了
}
}
}
void out()
{
ans = inf;
for1(i,0,m)//换几次都得
ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
printf("%.2lf", ans);
}
int main()
{
in();
floyd();
cl();
out();
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库