[关闭]
@rebirth1120 2019-07-15T08:15:58.000000Z 字数 998 阅读 602

[TJOI2015]组合数学

各省省选 dp


题目链接:[TJOI2015]组合数学
参考资料:题解 P3974 【[TJOI2015]组合数学】

题目大意:

现有一个 的网格,每个网格都有一定数量的宝藏,格点 的宝藏数量为
从格点 出发,只能往下或往右走,且每次经过一个格点只能取走该格点的一个宝藏,求最少要出发多少次才能取完所有宝藏。

解题思路:

这题突破点在于对网格的转化,一个只能往下或往右走的网格就相当于一个 DAG,那么求最少要走的次数就是求这个 DAG 的最小链覆盖。

Dilworth定理: DAG的最小链覆盖=最大点独立集

最小链覆盖: 指选出最少的链(可以重复)使得每个点都在至少一条链中

最大点独立集: 指最大的集合使集合中任意两点不可达

于是,问题就转化为求网格的最大点独立集。
已知,位于一个方格左下和右上的两个点是互不可达的,所以我们从右上向左下dp一边就行了。

注:我们将网格图转化为 DAG 是其实是将每一个权值作为一个点(如果一个点的权值为 5,那它就相当于 5 个点)。但是在dp时不需要将每一个点都考虑,只要加上网格中该节点的权值就好了。

状态转移方程:

(因为点 的情况可以从它上方或右方的节点继承,所以还要加一个

代码实现:

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #define ll long long
  5. using namespace std;
  6. const int N=1000+7;
  7. int T,n,m,a[N][N];
  8. ll f[N][N];
  9. int main(){
  10. cin>>T;
  11. while(T--){
  12. memset(f,0,sizeof(f));
  13. scanf("%d%d",&n,&m);
  14. for(int i=1;i<=n;i++)
  15. for(int j=1;j<=m;j++)
  16. scanf("%d",&a[i][j]);
  17. for(int i=1;i<=n;i++)
  18. for(int j=m;j>=1;j--)
  19. f[i][j]=max(max(f[i-1][j],f[i][j+1]),f[i-1][j+1]+a[i][j]);
  20. printf("%lld\n",f[n][1]);
  21. }
  22. return 0;
  23. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注