Atcoder Educational DP Contest 题解
A - Frog 1/B - Frog 2
入门...
1 #include<cstdio> 2 #define abs(a) ((a)>=0?(a):(-(a))) 3 #define min(a,b) ((a)<(b)?(a):(b)) 4 #define maxn 100050 5 using namespace std; 6 int dp[maxn],a[maxn]; 7 int main(){ 8 int n,k=2; 9 scanf("%d",&n); 10 for (int i=1;i<=n;i++) 11 scanf("%d",&a[i]),dp[i]=1e9; 12 dp[1]=0; 13 for (int i=2;i<=n;i++){ 14 for (int j=1;j<=k;j++) 15 if (i-j>0) dp[i]=min(dp[i],dp[i-j]+abs(a[i-j]-a[i])); 16 } 17 printf("%d\n",dp[n]); 18 return 0; 19 }
C - Vacation
$dp[i][0/1/2]$ 表示到第 $i$ 个 这一个选 $0/1/2$ 转移就很显然了....
1 #include<cstdio> 2 #define max(a,b) ((a)>(b)?(a):(b)) 3 #define maxn 100050 4 using namespace std; 5 int dp[maxn][3]; 6 int main(){ 7 int n; 8 scanf("%d",&n); 9 for (int i=1;i<=n;i++){ 10 int a,b,c; 11 scanf("%d%d%d",&a,&b,&c); 12 dp[i][0]=max(dp[i-1][1],dp[i-1][2])+a; 13 dp[i][1]=max(dp[i-1][0],dp[i-1][2])+b; 14 dp[i][2]=max(dp[i-1][0],dp[i-1][1])+c; 15 } 16 printf("%d\n",max(max(dp[n][0],dp[n][1]),dp[n][2])); 17 return 0; 18 }
D - Knapsack 1
裸背包
1 #include<cstdio> 2 #define ll long long 3 #define max(a,b) (a>b?a:b) 4 #define maxn 100050 5 using namespace std; 6 ll dp[maxn]; 7 int main(){ 8 int n,W; 9 scanf("%d%d",&n,&W); 10 for (int i=1;i<=n;i++){ 11 int a,b; 12 scanf("%d%d",&a,&b); 13 for (int j=W;j>=a;j--) 14 dp[j]=max(dp[j-a]+b,dp[j]); 15 } 16 ll ans=0; 17 for (int j=0;j<=W;j++) 18 if (dp[j]>ans) ans=dp[j]; 19 printf("%lld\n",ans); 20 return 0; 21 }
E - Knapsack 2
发现价值比较小,那么换一下 $dp$ 的状态表示
$dp[j]$ 表示价值为 $j$ 的最小重量
答案就从大到小枚举 $\leq W$ 的即可。
1 #include<cstdio> 2 #define ll long long 3 #define max(a,b) (a>b?a:b) 4 #define maxn 100050 5 #define min(a,b) (a<b?a:b) 6 using namespace std; 7 ll dp[maxn]; 8 int main(){ 9 int n,W; 10 scanf("%d%d",&n,&W); 11 for (int j=1;j<=1e5;j++) 12 dp[j]=1e12; 13 for (int i=1;i<=n;i++){ 14 int a,b; 15 scanf("%d%d",&a,&b); 16 for (int j=1e5;j>=b;j--) 17 dp[j]=min(dp[j-b]+a,dp[j]); 18 } 19 for (int i=1e5;i>=0;i--) 20 if (dp[i]<=W) return printf("%d\n",i),0; 21 return 0; 22 } 23 /* 24 dp[i][j]表示前i个价值和为j的最小重量 25 */
F - LCS
裸的最长公共子序列...方案存一下转移路径倒推就好了
1 #include<cstdio> 2 #include<cstring> 3 #define maxn 3005 4 using namespace std; 5 int dp[maxn][maxn],last[maxn][maxn]; 6 char s[maxn],t[maxn],w[maxn]; 7 int main(){ 8 scanf("%s%s",s+1,t+1); 9 int n=strlen(s+1),m=strlen(t+1); 10 for (int i=1;i<=n;i++) 11 for (int j=1;j<=m;j++){ 12 if (s[i]==t[j]) { 13 if (dp[i][j]<dp[i-1][j-1]+1) dp[i][j]=dp[i-1][j-1]+1,last[i][j]=1; 14 } 15 if (dp[i-1][j]>dp[i][j]) dp[i][j]=dp[i-1][j],last[i][j]=2; 16 if (dp[i][j-1]>dp[i][j]) dp[i][j]=dp[i][j-1],last[i][j]=3; 17 } 18 int x=n,y=m; 19 while (dp[x][y]){ 20 if (last[x][y]==1) w[dp[x][y]]=s[x],x--,y--;else 21 if (last[x][y]==2) x--;else y--; 22 } 23 for (int i=1;i<=dp[n][m];i++) 24 printf("%c",w[i]); 25 printf("\n"); 26 return 0; 27 }
G - Longest Path
最长路径,拓扑上dp
1 #include<cstdio> 2 #define maxn 100050 3 #define max(a,b) ((a)>(b)?(a):(b)) 4 using namespace std; 5 struct enode{ 6 int nxt,y; 7 }e[maxn]; 8 int ans=0,tot=0; 9 int n,m; 10 int q[maxn],dp[maxn],first[maxn],goin[maxn]; 11 void adde(int x,int y){ 12 e[tot].nxt=first[x]; 13 e[tot].y=y; 14 first[x]=tot++; 15 goin[y]++; 16 } 17 void tupu(){ 18 int head=1,tail=0; 19 for (int i=1;i<=n;i++) 20 if (!goin[i]) q[++tail]=i; 21 while (head<=tail){ 22 int x=q[head++]; 23 if (dp[x]>ans) ans=dp[x]; 24 for (int i=first[x];i>=0;i=e[i].nxt){ 25 int y=e[i].y; 26 goin[y]--; 27 dp[y]=max(dp[x]+1,dp[y]); 28 if (!goin[y]) q[++tail]=y; 29 } 30 } 31 } 32 int main(){ 33 34 scanf("%d%d",&n,&m); 35 for (int i=1;i<=n;i++) 36 first[i]=-1; 37 for (int i=1;i<=m;i++){ 38 int x,y; 39 scanf("%d%d",&x,&y); 40 adde(x,y); 41 } 42 tupu(); 43 printf("%d\n",ans); 44 return 0; 45 }
H - Grid 1
唔...入门dp吧QAQ
1 #include<cstdio> 2 #define HR 1000000007 3 using namespace std; 4 char s[1005]; 5 int dp[1005][1005]; 6 int main(){ 7 int n,m; 8 scanf("%d%d",&n,&m); 9 dp[1][1]=1; 10 for (int i=1;i<=n;i++){ 11 scanf("%s",s+1); 12 for (int j=1;j<=m;j++) 13 if ((i!=1||j!=1)&&(s[j]!='#')) dp[i][j]=(dp[i-1][j]+dp[i][j-1])%HR; 14 } 15 printf("%d\n",dp[n][m]); 16 return 0; 17 }
I - Coins
概率dp
$dp[i][j]$ 表示前 $i$ 个有 $j$ 个向上的概率
那么对于当前这一个 要不然就向上 要不然就向下
$dp[i][j]=dp[i-1][j-1]*p[i]+dp[i-1][j]*(1-p[i])$
1 #include<cstdio> 2 using namespace std; 3 double p[3005],dp[3005][3005]; 4 int main(){ 5 int n; 6 scanf("%d",&n); 7 for (int i=1;i<=n;i++) 8 scanf("%lf",&p[i]); 9 dp[0][0]=1; 10 for (int i=1;i<=n;i++){ 11 dp[i][0]=dp[i-1][0]*(1-p[i]); 12 for (int j=1;j<=i;j++) 13 dp[i][j]=dp[i-1][j-1]*p[i]+dp[i-1][j]*(1-p[i]); 14 } 15 16 double ans=0; 17 for (int i=1;i<=n;i++) 18 if (i>n-i) ans+=dp[n][i]; 19 printf("%.10lf\n",ans); 20 return 0; 21 }
J - Sushi
期望dp
$dp[i][j][k]$ 表示 $1$ 有 $i$ 个,$2$ 有 $j$ 个,$3$ 有 $k$ 个的期望
$dp[i][j][k]=dp[i-1][j][k]\times \frac{i}{n}+dp[i+1][j-1][k]\times \frac{j}{n}+dp[i][j+1][k-1]\times \frac{k}{n}+dp[i][j][k]\times \frac{n-i-j-k}{n}+1$
$dp[i][j][k]\times \frac{i+j+k}{n}=dp[i-1][j][k]\times \frac{i}{n}+dp[i+1][j-1][k]\times \frac{j}{n}+dp[i][j+1][k-1]\times \frac{k}{n}+1$
$dp[i][j][k]\times(i+j+k)=dp[i-1][j][k]\times i+dp[i+1][j-1][k]\times j+dp[i][j+1][k-1]\times k+n$
$dp[i][j][k]=dp[i-1][j][k]\times \frac{i}{i+j+k}+dp[i+1][j-1][k]\times \frac{j}{i+j+k}+dp[i][j+1][k-1]\times \frac{k}{i+j+k}+\frac{n}{i+j+k}$
1 #include<cstdio> 2 using namespace std; 3 double dp[305][305][305]; 4 int a[5]; 5 int main(){ 6 int n; 7 scanf("%d",&n); 8 for (int i=1;i<=n;i++){ 9 int x; 10 scanf("%d",&x); 11 a[x]++; 12 } 13 for (int k=0;k<=n;k++) 14 for (int j=0;j<=n;j++) 15 for (int i=0;i<=n;i++) 16 if (i||j||k) { 17 if (i) dp[i][j][k]+=dp[i-1][j][k]*i/(i+j+k); 18 if (j) dp[i][j][k]+=dp[i+1][j-1][k]*j/(i+j+k); 19 if (k) dp[i][j][k]+=dp[i][j+1][k-1]*k/(i+j+k); 20 dp[i][j][k]+=(double)n/(i+j+k); 21 } 22 printf("%.15lf\n",dp[a[1]][a[2]][a[3]]); 23 return 0; 24 } 25 26 /* 27 dp[i][j][k]表示当前有i个1 j个2 k个3 的期望步数 28 29 dp[0][0][0]=0 30 31 dp[i][j][k]=dp[i-1][j][k]*i/n+dp[i+1][j-1][k]*j/n+dp[i][j+1][k-1]*k/n+dp[i][j][k]*(n-i-j-k)/n+1 32 dp[i][j][k]*(i+j+k)/n=dp[i-1][j][k]*i/n+dp[i+1][j-1][k]*j/n+dp[i][j+1][k-1]*k/n+1 33 dp[i][j][k]*(i+j+k)=dp[i-1][j][k]*i+dp[i+1][j-1][k]*j+dp[i][j+1][k-1]*k+n 34 dp[i][j][k]=dp[i-1][j][k]*i/(i+j+k)+dp[i+1][j-1][k]*j/(i+j+k)+dp[i][j+1][k-1]*k/(i+j+k)+n/(i+j+k) 35 36 */