【51nod1705】七星剑(成环DP)
大致题意: 你要把一把剑从0星升至7星,有n颗宝石供你选择,第i颗宝石的价值是c[i],用第i颗宝石将剑从k-1星升至k星的成功率是prob[k][i],而失败后会掉lose[k][i],要你求出将剑升至7星的期望花费。
题解
看到这题,自然而然地就会想到用动态规划来做,而转移方程其实也很好推:
f[i]=min(f[i],f[i-1]+c[j]+(1-prob[i][j])*(f[i]-f[i-1-lose[i][j]));
其中f[i]表示将剑升至i星的期望花费。
就这么简单?
\(But\ wait\ a\ minute...\)
在转移方程中左边和右边同时出现了\(f[i]\)!
这就是传说中的成环\(DP\)。
那么成环\(DP\)该怎么做呢?
其实在这道题目中有一个很简单的方法:移项。没错,就是我们初一上学期就接触过的移项。
通过移项,原转移方程就变成了
f[i]=min(f[i],(f[i-1]+c[j]-(1-prob[i][j])*f[i-1-lose[i][j]])/prob[i][j]);
这样不就直接水过了吗!(顺便吐槽一下\(N≤100\)这样的数据范围真是太水了)
代码
#include<bits/stdc++.h>
#define LL long long
#define min(x,y) ((x)<(y)?(x):(y))
#define N 100
using namespace std;
int n,c[N+5],lose[10][N+5];
double prob[10][N+5],f[10];
int read()
{
int x=0,f=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') f=-1,ch=getchar();
while(ch>='0'&&ch<='9') (x*=10)+=ch-'0',ch=getchar();
return x*f;
}
int main(register int i,register int j,bool flag,bool ff)
{
for(n=read(),i=1;i<=n;c[i++]=read());
for(i=1,ff=true;i<=7;(flag?0:ff=false),++i)
for(j=1,flag=false;j<=n;scanf("%lf",&prob[i][j]),flag|=prob[i][j++]>0.0);
if(!ff) return puts("-1"),0;//判断是否存在不可能的情况
for(i=1;i<=7;++i)
for(j=1;j<=n;lose[i][j++]=read());
for(i=1;i<=7;++i)//DP过程,理解了再打真的很简单
for(f[i]=1e18,j=1;j<=n;++j)
f[i]=min(f[i],(f[i-1]+c[j]-(1-prob[i][j])*f[i-1-lose[i][j]])/prob[i][j]);
return printf("%.8lf",f[7]),0;
}
待到再迷茫时回头望,所有脚印会发出光芒