有环图上的动态规划总结

对于状态转移有环的动态规划,使用如下方式求解:

  1. 若转移里没有max/min,只有加减乘除运算,可以建立方程组,通过高斯消元在$O(n^3)$得出解。
  2. 若转移只是对一些元素取max/min(或第k大/小值),再加/减一个值,可以建图,使用dij或spfa求解。
  3. 若u依赖v,且若v能更新u,则dp[u]>=dp[v],且求的是min(例如$dp(u)=\min(S(u)+\sum dp[v],K(u))$)。
    每次:
    1:把值最小的点标记
    2:if 一个点u依赖的所有v都被标记
              计算dp(u),并标记u,
    3:回到1
    直到所有点被标记。
    就结束。

4.若2,3都不满足,可以使用如下方法:
先把每个点都入队,每次从队首取出u,并计算dp(u),若dp(u)发生更新,则把依赖u的v都加入队列(前提是v不在队列中)。
这个方法很像spfa,但时间复杂度约为$O(nm)$。
例题:tiger
老虎会堵住最小的,所以求的是次小值,是情况2,dij即可。
代码:

#include <stdio.h>
#include <queue>
using namespace std;
int fr[100010],ne[2000010];
int v[2000010],w[2000010],bs=0;
int bk[100010],zd[100010],N,M,K;
int ck[100010],inf=1e9;
int cd[100010];
struct SJd
{
    int cd,u;
    SJd(int Cd,int U)
    {
        cd=Cd;
        u=U;
    }
};
bool operator<(const SJd a,const SJd b)
{
    return a.cd>b.cd;
}
void addb(int a,int b,int c)
{
    v[bs]=b;
    w[bs]=c;
    ne[bs]=fr[a];
    fr[a]=bs;
    bs+=1;
}
int dij()
{
    priority_queue <SJd> pq;
    for(int i=0;i<N;i++)
        zd[i]=cd[i]=inf;
    for(int i=0;i<K;i++)
    {
        pq.push(SJd(0,ck[i]));
        cd[ck[i]]=zd[ck[i]]=0;
        bk[ck[i]]=1;
    }
    while(!pq.empty())
    {
        SJd he=pq.top();
        pq.pop();
        if(bk[he.u]==0)
        {
            bk[he.u]=1;
            continue;//忽略最小值
        }
        if(bk[he.u]==2)
            continue;
        bk[he.u]=2;
        for(int i=fr[he.u];i!=-1;i=ne[i])
        {
            int t=he.cd+w[i];
            if(t<zd[v[i]])
            {
                cd[v[i]]=zd[v[i]];
                zd[v[i]]=t;
                pq.push(SJd(t,v[i]));
            }
            else if(t<cd[v[i]])//求次小值
            {
                cd[v[i]]=t;
                pq.push(SJd(t,v[i]));
            }
        }
    }
    return cd[0];
}
int main()
{
    scanf("%d%d%d",&N,&M,&K);
    for(int i=0;i<N;i++)
        fr[i]=-1;
    for(int i=0;i<M;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        addb(a,b,c);
        addb(b,a,c);
    }
    for(int i=0;i<K;i++)
        scanf("%d",&ck[i]);
    printf("%d",dij());
    return 0;
}

例题2:[AHOI2014/JSOI2014]骑士游戏
使用方法3。

上一篇:P2672 推销员


下一篇:7-4 素数对猜想 (15 分)