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 记忆化搜索,如果你知道博弈论,那就很简单了。

猜你喜欢

转载自www.cnblogs.com/shanxieng/p/10232228.html