码迷,mamicode.com
首页 > 其他好文 > 详细

普及DP选做(持更)

时间:2020-06-28 15:02:47      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:单调队列   containe   com   rac   ble   name   des   暴力   位置   

目录


1

断环为链。
注意到两座仓库的距离不超过 \(\lfloor \frac{n-1}2 \rfloor\), 所以可以枚举算出每个仓库的最大可能代价, 然后用单调队列优化下就可以 \(O(N)\) 了(单调队列里的每个点都记录其 \(A\) 和其在链中的位置)。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+15;

int n, a[N*2+5];
int q[N*2+5], he, ta;
int ans=0;

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);
        a[n+i]=a[i];
    }
    q[he=ta=1] = 1;
    for(int i=2;i<=(n<<1);++i) {
        while(he<=ta && i-q[he]>(n-1)/2) ++he;
        if(he<=ta) ans=max(ans, a[i]+a[q[he]]+i-q[he]);
        while(he<=ta && a[q[ta]]-q[ta]<=a[i]-i) --ta;
        q[++ta] = i;
    }
    cout << ans;
    return 0;
}

2

首先建有向图, 边从先修课指向本课程, 发现建出了森林。
对每棵树分别求解在其中选 \([0,m]\) 个课程能拿到的最大得分, 最后用背包合并即可。
对每棵树求解的方法也用背包做。

#include<bits/stdc++.h>
using namespace std;
const int N = 305;

int n,m,sc[N];
vector<int> sons[N];
int f[N][N];
void sol(int x) {
    f[x][0] = 0;
    if(!sons[x].empty()) {
    for(int i=0;i<(int)sons[x].size();++i) {
        int y=sons[x][i];
        sol(y);
        for(int j=m;j>=1;--j)
            for(int k=1;k<=j;++k)
                f[x][j] = max(f[x][j], f[x][j-k]+f[y][k]);
    }
    }
    if(x!=0) {
        for(int i=m;i>=1;--i)
            f[x][i] = f[x][i-1]+sc[x];
    }
}

int main()
{
    scanf("%d%d", &n,&m);
    for(int i=1;i<=n;++i) {int fa;
        scanf("%d%d", &fa,&sc[i]);
        sons[fa].push_back(i);
    }
    sol(0);
    cout << f[0][m];
    return 0;
}

3

\(|D-P|\) 塞进状态里, 求最大 \(D+P\), 发现 \(最多状态数 = 4000*800\), 可做。
\(f[i][j][k]\) 表前 \(i\) 个, 选了 \(j\) 个, 差值为 \(k\)
最多转移总数也是 \(O(4000*800)\) 的。

#include<bits/stdc++.h>
using namespace std;
const int N = 205;
const int M = 25;
const int base = 400;

int n,m,p[N],d[N],sp[N],sd[N];
int f[N][M][805]; // 0为正, 1为负
int pre[N][M][805];
inline void cmax(int &x,int y) {x=max(x,y); }
inline bool ok(int k) {return k>=-400&&k<=400;}

void out(int i,int j,int k,int dd,int pp) {
    if(i==0&&j==0&&k==0) {
        printf("Best jury has value %d for prosecution and value %d for defence:\n",pp,dd);
        return;
    }
    if(pre[i][j][k+base]) {
        out(i-1,j-1,k-(d[i]-p[i]),dd+d[i],pp+p[i]);
        printf(" %d", i);
    } else {
        out(i-1,j,k,dd,pp);
    }
}
void sol() {
    memset(pre,0,sizeof pre);
    memset(f,-0x3f,sizeof f);
    f[0][0][0+base]=0;
    
    int ansi,ansj,ansk=10000,ansv=-1;
    for(int i=1;i<=n;++i) {
        for(int j=0;j<=min(i,m);++j) {
            for(int k=-400;k<=400;++k) {
                f[i][j][k+base] = f[i-1][j][k+base];
                pre[i][j][k+base] = 0;
                int prek = k-(d[i]-p[i]);
                if(j>0 && ok(prek) && f[i-1][j-1][prek+base]+(d[i]+p[i])>f[i][j][k+base]) {
                    f[i][j][k+base] = f[i-1][j-1][prek+base]+(d[i]+p[i]);
                    pre[i][j][k+base] = 1;
                    // cmax(f[i][j][k+base], f[i-1][j-1][prek+base]+(d[i]+p[i]));
                }
                if(j==m&&f[i][j][k+base]>=0) //cout<<k<<‘ ‘<<f[i][j][k+base]<<‘\n‘;
                {
                    if(abs(k)<abs(ansk)) {
                        ansv=f[i][j][k+base];
                        ansi=i,ansj=j,ansk=k;
                    }
                    else if(abs(k)==abs(ansk) && f[i][j][k+base]>ansv) {
                        ansv=f[i][j][k+base];
                        ansi=i,ansj=j,ansk=k;
                    }
                }
            }
        }
    }
    
    out(ansi,ansj,ansk,0,0);
    cout<<"\n\n";
}

int main()
{
    int id = 0;
    while(scanf("%d%d", &n,&m)==2 && n && m) {
        for(int i=1;i<=n;++i) { scanf("%d%d",&p[i],&d[i]);
            sp[i]=sp[i-1]+p[i], sd[i]=sd[i-1]+d[i];
        }
        printf("Jury #%d\n", ++id);
        sol();
    }
    return 0;
}

4

先随便求一个点的最大流,然后换根。
有点细节。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+15;

int n, deg[N];
int ct,hd[N],nt[N<<1],vr[N<<1],vl[N<<1];
void ad(int u,int v,int w) {
    vr[++ct]=v, vl[ct]=w, nt[ct]=hd[u], hd[u]=ct;
}

int f[N];
void dfs(int x,int fa) {
    f[x]=0;
    for(int i=hd[x];i;i=nt[i]) {int y=vr[i]; if(y==fa)continue;
        dfs(y,x);
        if(deg[y]==1&&y!=1) f[x] += vl[i];
        else f[x] += min(f[y], vl[i]);
    }
}
void getans(int x,int fa) {
    for(int i=hd[x];i;i=nt[i]) {int y=vr[i]; if(y==fa)continue;
        if(deg[x]==1) f[y] = f[y] + vl[i];
        else f[y] = f[y] + min(vl[i], f[x]-min(vl[i],f[y]));
        getans(y,x);
    }
}

int main()
{
    int T;cin>>T;while(T--) {
        scanf("%d",&n);
        ct=0;
        memset(deg,0,sizeof deg);
        memset(hd,0,sizeof hd);
        for(int i=1;i<n;++i) { int u,v,w;
            scanf("%d%d%d", &u,&v,&w); ad(u,v,w); ad(v,u,w);
            ++deg[u], ++deg[v];
        }
        dfs(1,0);
        getans(1,0);
        for(int i=2;i<=n;++i) f[i]=max(f[i],f[i-1]);
        cout<<f[n]<<‘\n‘;
    }
    return 0;
}

5

码农题。
暴力
\(f[i][k][l][r][0/1][0/1]\) 表示前 \(i\) 行选了 \(k\) 个, 第 \(i\) 行选了 \([l,r]\), 左右边界状态分别是 \(\{递增(1),递减(0)\}\) 之一时的最大权。
总状态数 \(1518750*2\), 加上转移要枚举 \(O(341718750*2)\) 次, 很难受。

\(\text{Rep(i,1,n);}\) 保平安。

#include<bits/stdc++.h>
using namespace std;
const int N = 16;
const int inf = 2100000000;
#define Rep(i,l,r) for(int i=(l);i<=(r);++i)

int n,m,k,sc[N][N],ssc[N][N];

int f[N][226][N][N][2][2];
int _pi[N][226][N][N][2][2], _pj[N][226][N][N][2][2], _pl[N][226][N][N][2][2];
int _pr[N][226][N][N][2][2], _px[N][226][N][N][2][2], _py[N][226][N][N][2][2];
inline void ud(int i,int j,int l,int r,int x,int y, int ii,int jj,int ll,int rr,int xx,int yy) {
    _pi[i][j][l][r][x][y] = ii;
    _pj[i][j][l][r][x][y] = jj;
    _pl[i][j][l][r][x][y] = ll;
    _pr[i][j][l][r][x][y] = rr;
    _px[i][j][l][r][x][y] = xx;
    _py[i][j][l][r][x][y] = yy;
}

int cost(int i,int l,int r) {return ssc[i][r]-ssc[i][l-1]; }

int ans,ansi,ansj,ansl,ansr,ansx,ansy;
bool vis[N][N];
void tag(int i,int j,int l,int r,int x,int y) {
    if(j==0) return;
    Rep(I,l,r) vis[i][I] = 1;
    tag(_pi[i][j][l][r][x][y], _pj[i][j][l][r][x][y], _pl[i][j][l][r][x][y], 
            _pr[i][j][l][r][x][y], _px[i][j][l][r][x][y], _py[i][j][l][r][x][y]);
}

int main() {
    scanf("%d%d%d",&n,&m,&k);
    Rep(i,1,n)Rep(j,1,m) {scanf("%d", &sc[i][j]); ssc[i][j]=sc[i][j]+ssc[i][j-1]; }
    
    Rep(i,0,n)Rep(j,1,k)Rep(l,1,m)Rep(r,l,m)Rep(x1,0,1)Rep(y1,0,1) f[i][j][l][r][x1][y1] = -inf;
    Rep(i,0,n)Rep(l,1,m)Rep(r,l,m)Rep(x1,0,1)Rep(y1,0,1) f[i][0][l][r][x1][y1] = 0;
    
    Rep(i,1,n)Rep(j,1,k)Rep(l,1,m)Rep(r,l,m) {
        if(j<r-l+1) continue;
        //x=0递增 y=0递增
        //由x=0/1, y=0 转移来
        Rep(pl,1,l)Rep(pr,l,r) {
            int &now = f[i][j][l][r][0][0];
            Rep(x1,0,1) if(now<f[i-1][j-(r-l+1)][pl][pr][x1][0]+cost(i,l,r)) {
                now = f[i-1][j-(r-l+1)][pl][pr][x1][0]+cost(i,l,r);
                ud(i,j,l,r,0,0,  i-1,j-(r-l+1),pl,pr,x1,0);
            }
        }
        //x=0递增 y=1递减
        //由x=0/1, y=0/1 转移来
        Rep(pl,1,l)Rep(pr,r,m) {
            int &now = f[i][j][l][r][0][1];
            Rep(x1,0,1)Rep(y1,0,1) if(now<f[i-1][j-(r-l+1)][pl][pr][x1][y1]+cost(i,l,r)) {
                now = f[i-1][j-(r-l+1)][pl][pr][x1][y1]+cost(i,l,r);
                ud(i,j,l,r,0,1,  i-1,j-(r-l+1),pl,pr,x1,y1);
            }
        }
        //x=1递减 y=0递增
        //由x=1 y=0 转移来
        Rep(pl,l,r)Rep(pr,pl,r) {
            int &now = f[i][j][l][r][1][0];
            if(now<f[i-1][j-(r-l+1)][pl][pr][1][0]+cost(i,l,r)) {
                now = f[i-1][j-(r-l+1)][pl][pr][1][0]+cost(i,l,r);
                ud(i,j,l,r,1,0,  i-1,j-(r-l+1),pl,pr,1,0);
            }
        }
        //x=1递减 y=1递减
        //由x=1 y=0/1转移来
        Rep(pl,l,r)Rep(pr,r,m) {
            int &now = f[i][j][l][r][1][1];
            Rep(y1,0,1) if(now<f[i-1][j-(r-l+1)][pl][pr][1][y1]+cost(i,l,r)) {
                now = f[i-1][j-(r-l+1)][pl][pr][1][y1]+cost(i,l,r);
                ud(i,j,l,r,1,1,  i-1,j-(r-l+1),pl,pr,1,y1);
            }
        }
        if(j==k) {
            Rep(x1,0,1)Rep(y1,0,1) if(ans<f[i][j][l][r][x1][y1]) {
              ans = f[i][j][l][r][x1][y1];
              ansi=i, ansj=j, ansl=l, ansr=r, ansx=x1, ansy=y1;
            }
        }
        
    }
    printf("Oil : %d\n", ans);
    tag(ansi,ansj,ansl,ansr,ansx,ansy);
    Rep(i,1,n)Rep(j,1,m) if(vis[i][j]) printf("%d %d\n",i,j);
    return 0;
}

普及DP选做(持更)

标签:单调队列   containe   com   rac   ble   name   des   暴力   位置   

原文地址:https://www.cnblogs.com/tztqwq/p/13198717.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!