Atcoder Educational DP Contest
阿新 • • 發佈:2019-01-07
前面簡單一點的題直接過吧。
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 記憶化搜尋,如果你知道博弈論,那就很簡單了。