A
/* 题目大意:一个人去买C瓶可乐,每瓶八块,有1 5 10面值的硬币若干,求出最少投币数。售货机会尽可能的找更少的硬币个数(也就是投一个十块三个一块,会找一个五块= =) 解题思路:dp[i][j][k],ijk记录三个硬币状态,dp记录使用个数,递归加优化 */ #include <cstdio> #include <iostream> #include <cmath> #include <algorithm> #include <cstring> using namespace std; int cokes,one,five,ten; int dp[700][200][100]; int vis[700][200][100]; int DP(int n,int a,int b,int c) { if(vis[a][b][c]==1) //访问过的直接return return dp[a][b][c]; else if(n==0) //全部买完 { vis[a][b][c]=1; dp[a][b][c]=0; return dp[a][b][c]; } else { dp[a][b][c] = 1000000; if(a>=8) dp[a][b][c] = min(dp[a][b][c],DP(n-1,a-8,b,c)+8); if(a>=3&&b>=1) dp[a][b][c] = min(dp[a][b][c],DP(n-1,a-3,b-1,c)+4); if(b>=2) dp[a][b][c] = min(dp[a][b][c],DP(n-1,a+2,b-2,c)+2); if(c>=1) dp[a][b][c] = min(dp[a][b][c],DP(n-1,a+2,b,c-1)+1); if(c>=1&&a>=3) dp[a][b][c] = min(dp[a][b][c],DP(n-1,a-3,b+1,c-1)+4); vis[a][b][c] = 1; return dp[a][b][c]; } } int main() { int T; cin>>T; while(T--) { cin>>cokes>>one>>five>>ten; memset(vis,0,sizeof(vis)); cout<<DP(cokes,one,five,ten)<<endl; } return 0; }
B
/* 题目大意:L先生喜欢用三根筷子(A<B<C),生日那天,他邀请其K人一起用,还有8个家人,共K+8人,要挑出K+8套筷子,保证有一个是最长的, 同时较短的两根的 badness:(A-B)^2最小,。T组数据,每组输入K(客人),N(筷子个数)以及N个筷子的长度(非递减顺序),输出所有badness的合的最小值。 解题思路:先把筷子长度倒序,保证每一套都会有一根最长的,然后遍历即可。 状态转移方程dp[i][j]=min(dp[i][j-1],dp[i-1][j-2]+chops[j]-chops[j-1]^2) 第i个人,第j根筷子 */ #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #include <iostream> using namespace std; int T,K,N; int chops[10000]; int dp[10000][10000]; int cmp(int a,int b) { return a>b; //a>b是逆序 } int main() { cin>>T; while(T--) { cin>>K>>N; for(int i = 1;i<=N;i++) scanf("%d",&chops[i]); sort(chops+1,chops+N+1,cmp); memset(dp,0,sizeof(dp)); for(int i = 1;i<=K+8;i++) { int t = i*3; dp[i][t] = dp[i-1][t-2]+(chops[t-1]-chops[t])*(chops[t-1]-chops[t]); //先挑出一套筷子 for(int j = t+1;j<=N;j++) { dp[i][j] = min(dp[i][j-1],dp[i-1][j-2]+(chops[j-1]-chops[j])*(chops[j-1]-chops[j])); //选第j根和不选第j根 } } cout<<dp[K+8][N]<<endl; } return 0; }
C
/* 题目大意:一个小姐姐被关在有R*C个小房间的迷宫里了,她要从左上角(1,1)逃到(R,C),每次移动可能向下,右或者留在原地,R行数据,每行三个浮点数代表原地,→,↓的概率 每次移动需要消耗两点法力,求需要多少法力才能出去 解题思路:概率DP,dp[i][j] = p[i][j][1]*dp[i][j] + p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2(其中p[i][j][k]代表在点(i,j)选择第k种走法的概率), 再化简一下:dp[i][j] = (p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2)/(1-p[i][j][1]) 因为dp[r][c]=0,即当在点(r,c)时,他不需要花费魔法值就可以到达(r,c),这样就可以从后往前递推了 */ #include <iostream> #include <cmath> #include <cstring> #include <algorithm> #include <cstdio> using namespace std; int R,C; double map[1010][1010][3]; double dp[1010][1010]; int main() { while(~scanf("%d%d",&R,&C)) { for(int i = 1;i<=R;i++) for(int j = 1;j<=C;j++) for(int k = 0;k<3;k++) scanf("%lf",&map[i][j][k]); memset(dp,0,sizeof(dp)); for(int i = R;i>=1;i--) for(int j = C;j>=1;j--) { if(i==R&&j==C) continue; if(fabs(map[i][j][0]-1)<1e-7) continue; dp[i][j] = (dp[i][j+1]*map[i][j][1]+dp[i+1][j]*map[i][j][2]+2)/(1-map[i][j][0]); } printf("%.3lf\n",dp[1][1]); } return 0; }
D
/* 题目大意:V个村庄(在x轴上)中建立P个邮局,求每个村庄到最近邮局的最短距离和 解题思路:区间DP,首先先求出在连续几个村庄建立一个邮局的最短距离,dis[i][j]表示村庄i到j建立一个邮局的距离则 dp[i][j]=dp[i][j-1]+x[j]-x[(i+j)/2] x[i]记录村庄坐标 所以对于dp 有在前k个村庄建立j-1个邮局,在k+1到i建立下一个邮局。用dp[i][j]表示前i个村庄建立j个邮局 即dp[i][j] = min(dp[i][j],dp[k][j-1]+dis[k+1][i]) */ #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> using namespace std; int v,p; int dp[310][50]; int dis[310][310]; int x[310]; int main() { cin>>v>>p; for(int i = 1;i<=v;i++) cin>>x[i]; memset(dp,0,sizeof(dp)); for(int i = 1;i<=v;i++) for(int j = i+1;j<=v;j++) { dis[i][j] = dis[i][j-1]+x[j]-x[(j+i)/2]; //i 到 j 村庄之间建立一个 postoffice 的 minimum distance } for(int i = 1;i<=v;i++) { dp[i][i] = 0; //自己村的邮局 dp[i][1] = dis[1][i]; //前i个村建立一个邮局 } for(int j = 2;j<=p;j++) for(int i = j+1;i<=v;i++) { dp[i][j] = 100000000; for(int k = j-1;k<i;k++) { dp[i][j] = min(dp[i][j],dp[k][j-1]+dis[k+1][i]); } } cout<<dp[v][p]<<endl; return 0; }
E
/* 题目大意:给一组括号,按照规则配对输出,尽可能少添加符号 解题思路:左右对应时,dp[i][j] = dp[i][j],用pos=-1记录 ,不对应时拆成两部分 dp[i][j] = min(dp[i][j],dp[i][mid]+dp[mid][j]) 用pos记录拆分点 */ #include <cstdio> #include <cmath> #include <algorithm> #include <iostream> #include <cstring> int dp[101][101],pos[101][101]; char str[101]; using namespace std; void print(int i,int j) { if(i>j) return; if(i==j) //仅有一个bracket { if(str[i]=='('||str[i]==')') printf("()"); else printf("[]"); } else if(pos[i][j]==-1) //左右对应 { printf("%c",str[i]); print(i+1,j-1); printf("%c",str[j]); } else //拆分成两部分 { print(i,pos[i][j]); print(pos[i][j]+1,j); } } int main() { scanf("%s",&str); int len = strlen(str); memset(dp,0,sizeof(dp)); for(int i = 0;i<len;i++) dp[i][i] = 1; for(int k = 1;k<len;k++) { for(int i = 0;i+k<len;i++) { int j = i+k; dp[i][j] = 1000000; if((str[i]=='('&&str[j]==')')||(str[i]=='['&&str[j]==']')) { dp[i][j] = dp[i+1][j-1]; pos[i][j] = -1; } for(int mid = i;mid<j;mid++) { if(dp[i][j]>(dp[i][mid]+dp[mid+1][j])) { dp[i][j] = dp[i][mid]+dp[mid+1][j]; pos[i][j] = mid; } } } } print(0,len-1); cout<<endl; //记得换行!!不然会wa.... return 0; }