首页 > 代码库 > 动态规划 试题收录

动态规划 试题收录

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;
}

 

动态规划 试题收录