题目
比赛
题意:
有m座城市w种天气,已知每天从城市i到城市j的概率,和每座城市每种天气的概率。已知n天的行程每天的天气,求最有可能的这n天每天在哪座城市的序列。多组解输出字典序最小的。
题解:
直接DP,一天天枚举从i到j地转移。由于n最大1000,直接乘精度受不了,最好取log,并且注意概率为0时返回结果为nan,所以自己手动设一个-INF吧。
比赛时3个人读题都看到了旅行了n+1天,并且第一天所在位置是固定的,但是找了很久都没找到在哪说了。可能是因为当时HDU已经跪了吧,第一次交是在还有一个半小时的时候,等到4点才返回个RE,但是那是所有的status都是0ms0KB的RE。后来剩40分钟的时候又交,一直没结果。所以大家都懒得再想了,想出了也不能交……很神奇的是吃完饭回来一眼就看到题目说了第一天是在第0座城市,加上这句再交果然过了。
//Time:343ms //Memory:2208KB //Length:1875B #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <map> using namespace std; #define MP(x,y) make_pair(x,y) const int MAXN= 110; const double EPS = 1e-14; const double INF = 1e50; long double dp[1010][MAXN]; int fa[1010][MAXN],ord[1010]; double pm[MAXN][MAXN],pw[MAXN][MAXN]; int main() { //freopen("/home/moor/Code/input","r",stdin); int ncase,n,m,w; scanf("%d",&ncase); while(ncase--) { scanf("%d%d%d",&n,&m,&w); for(int i=0;i<n;++i) scanf("%d",&ord[i]); for(int i=0;i<m;++i) for(int j=0;j<m;++j) { scanf("%lf",&pm[i][j]); if(pm[i][j]>EPS) pm[i][j]=log(pm[i][j]); else pm[i][j]=-INF; } for(int i=0;i<m;++i) for(int j=0;j<w;++j) { scanf("%lf",&pw[i][j]); if(pw[i][j]>EPS) pw[i][j]=log(pw[i][j]); else pw[i][j]=-INF; } for(int i=0;i<m;++i) dp[n-1][i]=pw[i][ord[n-1]]; for(int i=n-2;i>=0;--i) { for(int j=0;j<m;++j) dp[i][j]=-INF; for(int j=0;j<m;++j) if(pw[j][ord[i]]>-INF+EPS) for(int k=0;k<m;++k) if(pm[j][k]>-INF+EPS&&dp[i][j]+EPS<dp[i+1][k]+pm[j][k]+pw[j][ord[i]]) dp[i][j]=dp[i+1][k]+pm[j][k]+pw[j][ord[i]],fa[i][j]=k; } int pos=0; long double best=-INF; for(int i=0;i<m;++i) if(best+EPS<dp[0][i]+pm[0][i]) best=dp[0][i]+pm[0][i],pos=i; printf("%d",pos); for(int i=0;i<n-1;++i) printf(" %d",pos=fa[i][pos]); printf("\n"); } return 0; }