SRM588 Div1Medium KeyDungeonDiv1

来源:互联网 发布:深圳证券交易所软件 编辑:程序博客网 时间:2024/05/22 11:30

【分析】
又是一道状压dp,不解释。
如果没有内存限制: 64 MB的话,这道题应该说起来是很水的。可惜没如果。
我们来一步一步的理清dp的状态:

1、

定义bool dp[i][j][k][z],表示已经去了i集合中的门,有j、k、z把红、绿、白钥匙,这种情况是否存在。转移其实是比较简单的,记忆化写起来很舒服。

2、

定义int dp[i][j][k],表示已经去了i集合中的门,有j、k把红、绿钥匙,白钥匙最多个数为dp[i][j][k]。

很无奈,这些定义还是会超出空间。
最后还是看了题解,于是就有了定义3

3、

定义int dp[i][j],表示已经去了i集合中的门,有j把红钥匙,最少有dp[i][j]把,言下之意就是dp转移在i,j的情况下白色的万能钥匙最多。

然而空间太小,不能将信息存在数组下,所以我们只能每次对于i,j状态,去算此时的白钥匙,然后去判断是否能转移到下一扇门。
看来脑洞还是不太大啊。

【代码】

#include <bits/stdc++.h>using namespace std;#define M 12#define oo 1e9int dp[1<<M][135];int n,ans;int r,g,w;int R[M],G[M];int DR[M],DG[M],DW[M];void chk(int &x,int y){    if(x>y)x=y;}int main(){    scanf("%d",&n);    for(int i=0;i<n;i++)scanf("%d",&R[i]);    for(int i=0;i<n;i++)scanf("%d",&G[i]);    for(int i=0;i<n;i++)scanf("%d",&DR[i]);    for(int i=0;i<n;i++)scanf("%d",&DG[i]);    for(int i=0;i<n;i++)scanf("%d",&DW[i]);    scanf("%d %d %d",&r,&g,&w);    for(int i=0;i<(1<<n);i++){        for(int j=0;j<=130;j++)dp[i][j]=oo;    }    dp[0][r]=g;    for(int i=0;i<(1<<n);i++){        for(int j=0;j<=130;j++){//red            if(dp[i][j]==oo)continue;            int sum=r+g+w;            for(int k=0;k<n;k++){                if(i&(1<<k))sum+=-R[k]-G[k]+DR[k]+DG[k]+DW[k];            }            ans=max(ans,sum);            int nr=j;            int ng=dp[i][j];            int nw=sum-nr-ng;            for(int k=0;k<n;k++){                if(i&(1<<k))continue;                if(max(R[k]-nr,0)+max(G[k]-ng,0)>nw)continue;                chk(dp[i|(1<<k)][max(nr-R[k],0)+DR[k]],max(ng-G[k],0)+DG[k]);            }        }    }    printf("%d\n",ans);    return 0;}
2 0