求调DP,已调4小时
查看原帖
求调DP,已调4小时
413905
Nicolas192837楼主2024/8/3 19:55

https://www.luogu.com.cn/discuss/new?forum=CF1866E 这个题是我在一个小赛上遇到的(唯一改变的条件就是N,最大为300),赛后调题始终调不出来(结果在底部),请各位大佬帮忙
目前感觉是DP转移可能有问题,尤其可能是ST表访问代入值有问题,但是没检查出来

#include<bits/stdc++.h>
using namespace std;
long long read()
{
    long long x=0;
    short f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        {
            f*=(-1);
        }
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
void wit(long long x)
{
    if(x==0)
    {
        return ;
    }
    wit(x/10);
    putchar(x%10+'0');
    return ;
}
void write(long long x,bool edl)
{
    if(x<=0)
    {
        if(x==0)
        {
            putchar('0');
        }else{
            putchar('-');
            wit(x*(-1));
        }
    }else{
        wit(x);
    }
    if(edl)
    {
        putchar('\n');
    }else{
        putchar(' ');
    }
}
void test(bool end)
{
    puts("TEST");
    if(end)
    {
        exit(0);
    }
    return ;
}
const long long LLM=1e16;
long long upd[2][305][305][6],dp[2][305][305][6],st[3][200005][20][2],a[3][305],ans=LLM+1;
//关于upd,这是个标记更新时间的函数,防止滚动数组访问之前的数据
//dp[滚动数组的一位][一个电梯上次执行任务的时间][另一个电梯上次执行任务的时间][剩下那个去执行上次的任务的电梯编号]
//st[电梯编号(0~2)][启示位置][长度是2的多少幂次][方向]
//a[电梯编号][天数]
int n,q;
vector<pair<long long,long long> >li;
//li表示任务列表,没有任务的一天会用<LLM,LLM>代替
vector<int>lg2;
long long mi(int l,int r,int i)
{
    if(r<l)
    {
        swap(r,l);
    }
    return min(st[i][l][lg2[r-l+1]][0],st[i][r][lg2[r-l+1]][1]);
}//st表查询函数
void up(int i,int x,int y,int e,long long a)
{
    if(upd[i%2][x][y][e]<i)
    {
        dp[i%2][x][y][e]=a;
        upd[i%2][x][y][e]=i;
    }else{
        dp[i%2][x][y][e]=min(dp[i%2][x][y][e],a);
    }
    return ;
}//dp更新函数,可以检查dp值是否过期
int main()
{
    //freopen("elevator.in","r",stdin);
    //freopen("elevator.out","w",stdout);
    long long ai,opt,l,r,las,lasi,laslasi;
    bool stp[3]={false};
    n=read();
    q=read();
    for(int i=1;i<=q;i++)
    {
        ai=read();
        a[0][i]=ai;
        a[1][i]=ai;
        a[2][i]=ai;
    }
    lg2.push_back(0);
    lg2.push_back(0);
    for(int i=2;i<=q+1;i++)
    {
        lg2.push_back(lg2[i >> 1]+1);
    }
    li.push_back(make_pair(0,1));
    for(int i=1;i<=q;i++)
    {
        opt=read();
        if(opt==1)
        {
            l=read();
            r=read();
        }else{
            l=read();
            stp[l-1]=!stp[l-1];
            l=LLM;
            r=LLM;
        }
        li.push_back(make_pair(l,r));
        if(stp[0])
        {
            a[0][i]=INT_MAX;
        }
        if(stp[1])
        {
            a[1][i]=INT_MAX;
        }
        if(stp[2])
        {
            a[2][i]=INT_MAX;
        }//对于关闭的电梯,将该电梯在改天的费用改为INT_MAX
    }
    for(int i=0;i<=2;i++)
    {
        st[i][0][0][0]=INT_MAX;
        st[i][0][0][1]=INT_MAX;
        //第0天是不能调控电梯的,所以费用为无限大
        for(int j=1;j<=q;j++)
        {
            st[i][j][0][0]=a[i][j];
            st[i][j][0][1]=a[i][j];
        }
        for(int len=1;(1 << len)<=n+1;len++)
        {
            for(int j=0;j<=n-(1 << len)+1;j++)
            {
                st[i][j][len][0]=min(st[i][j][len-1][0],st[i][j+(1 << len)-1][len-1][1]);
                st[i][j+(1 << len)-1][len][1]=min(st[i][j][len-1][0],st[i][j+(1 << len)-1][len-1][1]);
            }
        }
    }
    for(int i=0;i<=2;i++)
    {
        for(int x=0;x<=q;x++)
        {
            for(int y=0;y<=q;y++)
            {
                dp[0][x][y][i]=LLM;
                dp[1][x][y][i]=LLM;
                upd[0][x][y][i]=-305;
                upd[1][x][y][i]=-305;
            }
//状态初始化(DP的赋值在实际应用里是无效的,但是可以用来调试,如果转移顺序有问题,结果会变成1e16+1
        }
    }
    dp[0][0][0][0]=0;
    dp[0][0][0][1]=0;
    dp[0][0][0][2]=0;
    upd[0][0][0][0]=0;
    upd[0][0][0][1]=0;
    upd[0][0][0][2]=0;//对合理值进行初始化
    las=1;//上一次执行后电梯的位置
    lasi=0;//上一次执行任务的那一天
    laslasi=0;//上上次执行任务的那一天
    //之所以有laslasi这个变量,是因为lasi本身就是隐藏在i里面的,所以下面的x和y只需要到laslasi就行了
    for(int i=0;i<q;i++)
    {
        if(li[i+1].first==LLM)
        {
            for(int x=0;x<=laslasi;x++)
            {
                if(li[x].first==LLM)
                {
                    continue;
                }//防止调用没有任务的一天
                for(int y=0;y<=laslasi;y++)
                {
                    if(li[y].first==LLM)
                    {
                        continue;
                    }
                    for(int e=0;e<=2;e++)
                    {
                        if(upd[i%2][x][y][e]<i)
                        {
                            continue;
                        }//检查是否为合理(及时更新)的值
                        up(i+1,x,y,e,dp[i%2][x][y][e]);
                    }
                }
            }
        }else{
            for(int x=0;x<=laslasi;x++)
            {
                if(li[x].first==LLM)
                {
                    continue;
                }
                for(int y=0;y<=laslasi;y++)
                {
                    if(li[y].first==LLM)
                    {
                        continue;
                    }
                    for(int e=0;e<=2;e++)
                    {
                        if(upd[i%2][x][y][e]<i)
                        {
                            continue;
                        }
                        up(i+1,x,y,e,dp[i%2][x][y][e]+mi(lasi,i+1,e)*abs(las-li[i+1].first)+abs(li[i+1].first-li[i+1].second)*a[e][i+1]);
                        up(i+1,lasi,x,(e+2)%3,dp[i%2][x][y][e]+mi(y,i+1,(e+2)%3)*abs(li[y].second-li[i+1].first)+a[(e+2)%3][i+1]*abs(li[i+1].first-li[i+1].second));
                        up(i+1,y,lasi,(e+1)%3,dp[i%2][x][y][e]+mi(x,i+1,(e+1)%3)*abs(li[x].second-li[i+1].first)+a[(e+1)%3][i+1]*abs(li[i+1].first-li[i+1].second));
//dp的第四维其实还决定着前两维的信息
//第四维的电梯记为e号电梯(e=0,1,2)
//则第二维是第(e+1)%3号电梯上一次执行任务的那一天
//第三维是第(e+2)%3号电梯上一次执行任务的那一天
//顺次转移就有了如上的dp式子
                    }
                }
            }
            laslasi=lasi;
            lasi=i+1;
            las=li[i+1].second;
        }
    }
    for(int x=0;x<q;x++)
    {
        for(int y=0;y<q;y++)
        {
            for(int e=0;e<=2;e++)
            {
                if(upd[q%2][x][y][e]==q)
                {
                    ans=min(ans,dp[q%2][x][y][e]);
                }
            }
        }
    }
    write(ans,true);
    return 0;
}

2024/8/3 19:55
加载中...