动态规划 试题收录
DP稀烂也不是一天两天了
我觉得我很有必要把做(chao)过的DP记录下来啊... ...DP这种精细的东西真是太玄妙了。
慢慢来吧。只有思维足够强大,才足以见题拆题。
编辑中... ...
树形DP:
1.机器人采集金属
分析:设 f[i][j] 为:遍历完j的子树,且剩下i个回到j的最小代价。特殊的,f[0][i]表示用一个机器人走完所有节点再回来的代价,原因是每个点都必须且只能转移一种对答案贡献的状态。
那么状态转移方程就要分两类讨论。
初始化:f[i][x]=sigma(f[0][son_i]);
维护:f[i][k]=min(f[i][k],f[i-j][son_i]+f[j][son_i]+j*Edge_val;
想想真的很妙啊。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
using namespace std;
const int N = 100010;
struct Node{int to;LL val;int next;}E[N];
int n,S,K,tot,head[N];LL f[21][N];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
inline void link(int u,int v,LL w)
{
E[++tot]=(Node){v,w,head[u]};
head[u]=tot;
}
inline void dfs(int x,int fa)
{
for(int e=head[x];e;e=E[e].next)
{
int now=E[e].to;
if(now==fa)continue;
dfs(now,x);
for(int k=K;k>=0;--k)
{
f[k][x]+=f[0][now]+2*E[e].val;
for(int i=1;i<=k;++i)
f[k][x]=min(f[k][x],f[k-i][x]+f[i][now]+i*E[e].val);
}
}
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
n=gi();S=gi();K=gi();
for(int i=1;i<n;++i)
{
int u=gi(),v=gi();LL w=gL();
link(u,v,w);link(v,u,w);
}
dfs(S,S);printf("%lld\n",f[K][S]);
/*fclose(stdin);
fclose(stdout);*/
return 0;
}
背包问题
1.多重背包计数
考场上只打了一个01背包的暴力,但是明显没什么卵用。
然后好像RG说是FFT?弱的抠脚... ...
不过Nick讲了一种十分高明的做法:分块。
考场上我在想能不能直接把N/2的枝减掉,Nick说我可以大胆剪刀根号级。
对于根号之前的就直接暴力肛。这样前半截的复杂度在数据不超过2000下没有压力(实在是不会分析了)。
对于根号后的,记录g[i][j]表示使用i个,体积和为j的方案数。然后显然i顶多到根号n。
转移方程也是十分巧妙,给我一种点科技树的感觉。
1.加入一个体积为根号的物品;
2.背包内每个东西的值都变成其+1;
是不是巧妙无比?我是被吓到了/*哦吼*/。对了记得g[1][0](G10);
统计答案的时候,用乘法原理+加法原理,前半部分和后半部分搞一下。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#include <cmath>
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
using namespace std;
const int N = 100010;
const int Mod = 23333333;
LL f[5][N],g[320][N],n,lim,sum[N],now;
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void pui(int x)
{
if(x<10)putchar(x+'0');
else pui(x/10),putchar(x%10+'0');
}
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
inline void work1()
{
for(int i=1;i<=lim;++i)
{
now^=1;memset(sum,0,sizeof(sum));
for(int j=0;j<=n;++j)
{
f[now][j]=(sum[j%i]+f[now^1][j])%Mod;
if(j<i*i)sum[j%i]=(sum[j%i]+f[now^1][j])%Mod;
else sum[j%i]=((sum[j%i]-f[now^1][j-i*i]+f[now^1][j])%Mod+Mod)%Mod;
}
}
}
inline void work2()
{
for(int i=0;i<=lim;++i)
for(int j=0;j<=n;++j)
{
if(i&&i+j<=n)g[i][i+j]=(g[i][i+j]+g[i][j])%Mod;
if(j+lim+1<=n)g[i+1][j+lim+1]=(g[i+1][j+lim+1]+g[i][j])%Mod;
}
++g[1][0];
}
inline void calcans()
{
LL Ans=0;
for(int i=0;i<=n;++i)
for(int j=1;j<=lim;++j)
Ans=(Ans+((LL)f[now][i]*(LL)g[j][n-i])%Mod)%Mod;
printf("%lld\n",Ans);
}
int main()
{
n=gi();lim=sqrt(n);f[0][0]=g[0][0]=1;
work1();work2();calcans();return 0;
}
数学等类
1.机器人m号
这题的正解有两种,DP和递推(其实也是一个DP)。关于第二种,我发现它的状态设置的十分巧妙,对欧拉函数的积性运用到了很高的境界。
容易发现独立数就是欧拉函数。所以答案就是要求一个数的所有因子:
1.由奇数个不同的奇质因子组成的因子的欧拉函数的和。
2.由偶数个不同的奇质因子组成的因子的欧拉函数的和。
3.其他所有因子的欧拉函数的和。
然后由欧拉函数的性质我们发现第3个不用求,重点在前两个。
用莫比乌斯函数推是没用的,因为没有2。可以考虑DP;
朴素的DP就是f[i][j]表示推到第i个质数,由j个不同奇质数组成的数的欧拉函数和,然后答案就是两个累加。这也是很巧妙的利用了积性。
第二种就更加简单粗暴,但这正是世界的美丽。设Ans1和Ans2分别就是f对应的奇偶状态的和。一路读入一路递推就可以了。递推式也简介务必让人shiniao未及,吓得我一抖一抖的。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
using namespace std;
const LL Mod = 10000;
LL F[1010][1010],Ans1,Ans2,m,k;
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void pL(LL x)
{
if(x<0)putchar('-'),pL(-x);
if(x<10)putchar(x+'0');
else pL(x/10),putchar(x%10+'0');
}
void pc(){putchar('\n');}
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
LL Qpow(LL d,LL z)
{
LL ans=1;
for(;z;z>>=1,d=d*d%Mod)if(z&1)ans=ans*d%Mod;
return ans;
}
int main()
{
k=gL();m=1;
for(LL i=1;i<=k;++i)
{
LL p=gL(),e=gL();
m=m*Qpow(p,e)%Mod;
if(p==2)continue;
LL ans1=(Ans1+(Ans2+1)*(p-1))%Mod;
LL ans2=(Ans2+Ans1*(p-1))%Mod;
Ans1=ans1;Ans2=ans2;
}
pL(Ans2);pc();pL(Ans1);pc();pL(((m-Ans1-Ans2-1)%Mod+Mod)%Mod);
return 0;
}

浙公网安备 33010602011771号