例题连接:https://ac.nowcoder.com/acm/problem/16428
/*少说话,多做事*/ #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <stack> #include <sstream> #include <map> #include <set> #include <queue> #include <stdlib.h> typedef long long ll; using namespace std; /*这n个时间段是连续的(要不然这道题咋做呀),就是安排的上课时间是连续的*/ const int maxn = 2009; const int maxv = 309; int e[maxv][maxv]; int c[maxn]; //储存牛牛被安排的教室 int d[maxn]; //另一间同一时间段的上课的教室 double k[maxn]; //申请成功的概率(在0~1范围内,0和1都可以取) double f[maxn][maxn][2]; int n, m, V, E; int a, b, w; void folyd() //求出各个点之间的最短路径,储存在e二维数组里面) { for (int k=1; k<=V; k++) for (int i=1; i<=V; i++) for (int j=1; j<=V; j++) e[i][j] = min(e[i][j], e[i][k]+e[k][j]); } int main() { cin >> n >> m >> V >> E; //分别表示:时间段的数量(n),最多可以申请更换多少节教室的数量(m),教室的数量(V),道路的数量(E) for (int i=1; i<=n; i++) scanf("%d", &c[i]); for (int i=1; i<=n; i++) scanf("%d", &d[i]); for (int i=1; i<=n; i++) scanf("%lf", &k[i]); memset(e, 0x3f3f3f3f, sizeof e); //将每一个教室到比的教室的距离都初始化为最大值:65 for (int i=1; i<=V; i++) e[i][i] = 0; //每一个教室到自己的距离都是0 for (int i=1; i<=E; i++) { scanf("%d%d%d", &a, &b, &w); //w的取值范围是1~100 e[a][b] = e[b][a] = min(e[a][b], w); //更新两个教室之间的距离 } folyd(); //求出各个点之间的最短路径,储存在e二维数组里面) for(int i=1;i<=n;i++)//手动memset,将 { for(int j=0;j<=m;j++) { for(int k=0;k<=1;k++) { f[i][j][k]=0x3f3f3f3f; } } } //f的当i=1的情况,因为j<=m&&j<=i,所以j的取值就是0和1 //当j=0的时候,就是没有一次的交换,所以后面k也不可能为1,(因为如果是1的话,代表有了交换,跟j=0没有交换矛盾),所以当j=0的时候,k只有一种情况,就是k=0 //同理当j=1的时候,k也只有一种情况就是k=1 //所以在定义边界条件的时候只需要定义两个f就可以了。 //f[i][j][k]表示的是:在第i个时间段(一共n个时间段)交换了j次教室(最多可以交换m次)k代表这次交换或者不交换 f[1][0][0] = f[1][1][1] = 0; //边界条件(具体为什么在上面有描述) for (int i=2; i<=n; i++) //第一维从第2开始,第一维的1已经被初始化过了 { for (int j=0; j<=m&&j<=i; j++) //因为最多m次交换,所以最多交换m次,因为i代表的是几个时间段,所以肯定不能超过时间段,所以j<=i { //在第i个时间段,不申请换教室 f[i][j][0] = min(f[i-1][j][0] + e[c[i-1]][c[i]] , //第i-1个时间段也不申请换教室(如果不交换教室的话,还是从c[i-1]到c[i]这条路径上课,当然走的是两者之间的最短路) f[i-1][j][1] //第i-1个时间段申请教室 + k[i-1] * e[d[i-1]][c[i]] //上一个点申请成功,申请成功的概率是k[i-1],并且上一个点的教室变成d[i-1],而不是c[i-1] + (1.0-k[i-1]) * e[c[i-1]][c[i]]); //上一个点申请失败,申请失败的概率是1.0-k[i-1],上一个点的教室不变 //在第i个时间段,申请换教室 if (j == 0) continue; f[i][j][1] = min( f[i-1][j-1][0] + k[i] * e[c[i-1]][d[i]] //第i-1个时间段不申请换教室,+ 第i个时间段申请成功和不成功 + (1.0-k[i]) * e[c[i-1]][c[i]], //第i-1个时间段内申请换教室,(不成功和成功)+ 第i个时间段申请成功和不成功 f[i-1][j-1][1] + k[i] * k[i-1] * e[d[i-1]][d[i]] //两次交换都成功 + k[i] * (1.0-k[i-1]) * e[c[i-1]][d[i]] //i成功,i-1不成功 + (1.0-k[i]) * k[i-1] * e[d[i-1]][c[i]] //i不成功,i-1成功 + (1.0-k[i]) * (1.0-k[i-1]) * e[c[i-1]][c[i]]); //都不成功 } } double ans = 9e99; for (int i=0; i<=m; i++) { ans = min(ans, min(f[n][i][0], f[n][i][1])); //最后肯定要到n个时间段,所以最后的遍历这里,第一维都是n,然后在从一共进行多少次交换入手,因为不一定换越多越好,每次都考虑换和不换两种情况 } printf("%.2lf\n", ans); return 0; }