写在最前面
这是今天的考试,一场比较水的考试,但是也不知为什么,就是爆炸了……
orz sillyf大佬,随随便便切掉了t3,然后就轻描淡写的说:“t3不就是一道很水的状压DP嘛?”
QAQ……
t1:#【LOJ】6162 「美团 CodeM 初赛 Round A」身体训练 模拟
虽然这题是一道暴力就过的水题,但是为了讲的更清楚一点,定义
f[i][j]
表示第
i
个人第为
附上AC代码:
#include <cstdio>
using namespace std;
const int N=1e3+10;
int n;
double v,u,c[N],d[N],ans;
int main(void){
scanf("%d%lf%lf",&n,&v,&u);
for (int i=1; i<=n; ++i) scanf("%lf",&c[i]);
for (int i=1; i<=n; ++i) scanf("%lf",&d[i]);
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
ans+=u/(c[i]-(j-1)*d[i]-v);
return printf("%.3lf\n",ans),0;
}
t2:#6178. 「美团 CodeM 初赛 Round B」景区路线规划 期望DP || 记忆化搜索
考试时的我就宛如一个智障,大概意思就是以为 n∗1n≠n 。考试真的是一种奇怪的东西,可以使人智熄……
发现这题就是一道非常裸的期望DP题,可惜并不会处理期望DP,只会记忆化搜索。
为了方便,我们可以建立一个虚点,和所有点建一条权值为零的边,然后直接搜索这个虚点就行了。
附上AC代码:
#include <cstdio>
using namespace std;
const int N=110;
struct side{
int from,to,w,nt;
}s[N*N];
int n,m,t,c[N],v[3][N],x,y,w,h[N],num;
double dp[N][500][3];
inline void add(int x,int y,int w){
s[++num]=(side){x,y,w,h[x]},h[x]=num;
if (x) s[++num]=(side){y,x,w,h[y]},h[y]=num;
}
inline double so(int now,int ti,int o){
if (dp[now][ti][o]) return dp[now][ti][o];
double ret=v[o][now];int cnt=0;
for (int i=h[now]; i; i=s[i].nt) if (ti>=s[i].w+c[s[i].to]) ++cnt;
for (int i=h[now]; i; i=s[i].nt) if (ti>=s[i].w+c[s[i].to]) ret+=so(s[i].to,ti-s[i].w-c[s[i].to],o)/cnt;
return dp[now][ti][o]=ret;
}
int main(void){
scanf("%d%d%d",&n,&m,&t);
for (int i=1; i<=n; ++i) scanf("%d%d%d",&c[i],&v[1][i],&v[2][i]),add(0,i,0);
for (int i=1; i<=m; ++i) scanf("%d%d%d",&x,&y,&w),add(x,y,w);
return printf("%.5lf %.5lf\n",so(0,t,1),so(0,t,2)),0;
}
t3:#6177. 「美团 CodeM 初赛 Round B」送外卖2 状压DP
首先我们可以用floyd预处理出所有节点之间的最短路。
然后我们定义 f[i][j] 表示现在在第 i <script type="math/tex" id="MathJax-Element-15">i</script>号节点,状态为j的最小时间。
j表示一个q位的三进制数,每一位表示一个外卖的状态,0表示该外卖还没有被取走,1表示该外卖已经被取走但还没有被送达,2表示该外卖已经被送达。
考虑状态之间的转移,0的时候就从当前节点到当前外卖的起点,1的时候就从当前节点到外卖的终点,注意时间边界的判断。
然后就在所有合法状态中找出送达外卖数的最大值即可。
附上AC代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,q,map[21][21],x,y,w,f[21][59050],pow[11],s[11],t[11],l[11],r[11];
int main(void){
scanf("%d%d%d",&n,&m,&q),memset(map,0x3f,sizeof map);
for (int i=1; i<=m; ++i) scanf("%d%d%d",&x,&y,&w),map[x][y]=min(map[x][y],w);
for (int i=1; i<=n; ++i) map[i][i]=0;
for (int k=1; k<=n; ++k)
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
for (int i=1; i<=q; ++i) scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]);
f[1][0]=0,pow[0]=1;
for (int i=1; i<=10; ++i) pow[i]=pow[i-1]*3;
for (int i=1; i<=n; ++i) for (int j=0; j<pow[q]; ++j) f[i][j]=1e9;
for (int i=1; i<=n; ++i) f[i][0]=map[1][i];
for (int i=0; i<pow[q]; ++i){
for (int j=1; j<=n; ++j)
for (int k=1; k<=q; ++k){
int num=i%pow[k]/pow[k-1];
if (!num)
if (f[j][i]+map[j][s[k]]>=l[k]) f[s[k]][i+pow[k-1]]=min(f[s[k]][i+pow[k-1]],f[j][i]+map[j][s[k]]);
else f[s[k]][i+pow[k-1]]=min(f[s[k]][i+pow[k-1]],l[k]);
if (num==1) if (f[j][i]+map[j][t[k]]<=r[k]) f[t[k]][i+pow[k-1]]=min(f[t[k]][i+pow[k-1]],f[j][i]+map[j][t[k]]);
}
}
int ans=0;
for (int i=0; i<pow[q]; ++i)
for (int j=1; j<=n; ++j)
if (f[j][i]!=1e9){
int t=0;
for (int k=1; k<=q; ++k) if (i%pow[k]/pow[k-1]==2) ++t;
ans=max(ans,t);
}
printf("%d\n",ans);
return 0;
}
写在最后
以后考试的时候一定要保持状态的良好,还有要合理分配时间,不要再次出现半个小时写t3的悲剧局面。