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;
}