1. 程式人生 > >Atcoder Educational DP Contest

Atcoder Educational DP Contest

前面簡單一點的題直接過吧。

A 暴力DP

B 怎麼還是暴力DP

C 還是暴力DP

D 直接揹包

E 這個揹包不太一樣了,這裡有一個技巧,就是因為價值很小,所以直接對價值揹包,求出來達到某一個權值最小的重量,然後找到滿足限制的最大的價值即可。注意,如果能達到權值比這個還大的點,那麼這個點很顯然也是可以達到的。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=210000;
const int inf=0x3f3f3f3f;

int n,w,tot;
ll f[Maxn],v;

signed main() {
//  freopen("test.in","r",stdin);
    read(n,tot);
    memset(f,0x3f,sizeof(f));f[0]=0;
    int now=0;
    for(int i=1;i<=n;i++) {
        read(w,v);
        for(int j=now+v;j>=v;j--) f[j]=min(f[j],f[j-v]+w);
        now+=v;
    }
    for(int i=now;i>=0;i--) f[i]=min(f[i],f[i+1]);
    int ans=0;
    while(f[ans]<=tot) ans++;
    printf("%d\n",ans-1);
    return 0;
}

F 套路題,統計答案略噁心,還是貼一下程式碼吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=3100;
const int inf=0x3f3f3f3f;

int f[Maxn][Maxn],pre[Maxn][Maxn],top;
char a[Maxn],s[Maxn],st[Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    scanf("%s%s",a,s);
    int n=strlen(a),m=strlen(s);
    for(int i=0;i<m;i++) if(a[0]==s[i]) f[0][i]=1;
    for(int i=0;i<m;i++) pre[0][i]=-1;
    for(int i=1;i<n;i++) {
        int now=0,las=-1;
        for(int j=0;j<m;j++) {
            if(a[i]==s[j]) {
                if(f[i-1][j]>now+1) {
                    now=f[i-1][j],las=(a[i-1]==s[j]?i-1:pre[i-1][j]);
                    pre[i][j]=las;
                    f[i][j]=now;
                }
                else {
                    pre[i][j]=las;
                    f[i][j]=now+1;
                    if(f[i-1][j]>now)
                        now=f[i-1][j],las=(a[i-1]==s[j]?i-1:pre[i-1][j]);
                }
            }
            else {
                if(f[i-1][j]>now)
                    now=f[i-1][j],las=(a[i-1]==s[j]?i-1:pre[i-1][j]);
                pre[i][j]=las;
                f[i][j]=now;
            }
        }
    }
    int ans=0,temp;
    for(int i=0;i<m;i++) if(f[n-1][i]>ans) {
        ans=f[n-1][i];
        temp=i;
    }
    if(ans==0) return 0;
    int nx,ny=temp;
    if(a[n-1]==s[temp]) nx=n-1;
    else nx=pre[n-1][temp];
    while(nx!=-1) {
        st[++top]=a[nx];
        nx=pre[nx][ny];
        int ans=0,temp;
        for(int i=0;i<ny;i++)
            if(f[nx][i]>ans) ans=f[nx][i],temp=i;
        ny=temp;
    }
    while(top) putchar(st[top--]);
    return 0;
}

G 直接DAG上的DP,太簡單了不放程式碼了。

H 每個點都是從上邊或左邊轉移即可。

I 概率DP,直接記有i個正面朝上的概率,然後就可以\(O(n^2)\)DP了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=11000;
const int inf=0x3f3f3f3f;
const int mod=1000000007;

int n;
double f[Maxn],p;

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    f[0]=1;
    for(int i=1;i<=n;i++) {
        scanf("%lf",&p);
        for(int j=i;j>=1;j--) f[j]=(f[j]*(1.0-p)+f[j-1]*p);
        f[0]*=1.0-p;
    }
    double ans=0;
    for(int i=n/2+1;i<=n;i++) ans+=f[i];
    printf("%.10lf",ans);
    return 0;
}

J 期望DP,因為每個數都很小,直接記目前剩一個,兩個,三個的分別有多少。好像從小到大轉移要方便一些。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=310;
const int inf=0x3f3f3f3f;
const int mod=1000000007;

int n,x,a[4];
double f[Maxn][Maxn][Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    for(int i=1;i<=n;i++) {
        read(x);
        a[x]++;
    }
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n-i;j++)
            for(int k=0;k<=n-i-j;k++) {
                if(!i&&!j&&!k) continue;
                double x=i+j+k,p=(double)n/x;
                if(i) f[i][j][k]+=f[i-1][j+1][k]*i/x;
                if(j) f[i][j][k]+=f[i][j-1][k+1]*j/x;
                if(k) f[i][j][k]+=f[i][j][k-1]*k/x;
                f[i][j][k]+=p;
            }
    printf("%.10lf",f[a[3]][a[2]][a[1]]);
    return 0;
}

K 記憶化搜尋,如果你知道博弈論,那就很簡單了。