首页 > 代码库 > CH Round #53 -密室

CH Round #53 -密室

描述

有N个密室,3种钥匙(红色,绿色,白色)和2种锁(红色,绿色),红色钥匙只能开红色的锁,绿色钥匙只能开绿色的锁,白色钥匙可以开红色的锁和绿 色的锁,一把钥匙使用一次之后会被扔掉。每个密室由一扇门锁着,上面锁着一些红色和绿色的锁,房间里面放着一些红色、绿色和白色的钥匙,打开密室你将拿走 这些钥匙。你可以以任意顺序打开密室,但是同一个密室只能打开一次。初始你已经有了一些钥匙了,现在你要进行决策,使得最终剩下的钥匙尽量的多,注意,你 不一定要打开所有的房间,你可以在任意时刻结束。输出你最多能有几把钥匙。

输入格式

第一行一个整数N,表示有N个密室。
第二行N个整数,第i个数表示第i扇门上有ai把红色的锁。
第三行N个整数,第i个数表示第i扇门上有bi把绿色的锁。
第四行N个整数,第i个数表示第i个密室有ci把红色的钥匙。
第五行N个整数,第i个数表示第i个密室有di把绿色的钥匙。
第六行N个整数,第i个数表示第i个密室有ei把白色的钥匙。
第七行三个整数k0,k1,k2,依次表示初始你拥有的红色、绿色、白色钥匙的数量。

输出格式

输出一个整数,表示你最多能有几把钥匙。

样例输入

31 2 30 4 90 0 100 8 91 0 83 1 2

样例输出

8

数据范围与约定

  • 对于40%的数据,满足N<=8。
  • 对于100%的数据,满足N<=14,0<=ai,bi,ci,di,ei,k0,k1,k2<=10。

题解:
刚才在与iwtwiioi的讨论中又有了新的发现,现在来写一下详细的题解。
考虑到打开门的集合一定,钥匙总数一定是一定的,因为一把钥匙开一把锁,那么是否可以直接以开门集合为状态进行转移呢?
答案是否定的。因为钥匙总数是一定的并不代表着红、蓝、白这三种钥匙各自的数量相同,比如你有5 0 9 能去打开0 10的门吗? 而4 1 9就可以。
这种情况为什么会发生?是因为开门顺序不同导致的。比如:
门1  开5 0  得0 0 2
门2  开2 0  的10 0 0
刚开始有 3 1 2
如果先开1,则有 0 1 2,再开2,有10 1 0    
如果先开2,则有11 1 2,再开2,有6 1 4
这样就出现了不同。
这样的话,如果后面还有
门3 开0 5 得 10 10 10
这样按1-2的顺序无法打开此门,显然不会是最优解

如果按2-1来打开,则可以打开门3,得到最优解。

所以,不能单单以经过门的集合,而应该加入红、蓝、白数量作为状态,这样的话那么就只能爆搜了。。。

BFS应该可过,数组开1000W。DFS可能由于需要调用系统栈而使时间变长,导致超时。
代码1:我的BFS

 1 #include<cstdio> 2  3 #include<cstdlib> 4  5 #include<cmath> 6  7 #include<cstring> 8  9 #include<algorithm>10 11 #include<iostream>12 13 #include<vector>14 15 #include<map>16 17 #include<set>18 19 #include<queue>20 21 #include<string>22 23 #define inf 100000000024 25 #define maxn 1000000026 27 #define maxm 500+10028 29 #define eps 1e-1030 31 #define ll long long32 33 #define pa pair<int,int>34 35 #define for0(i,n) for(int i=0;i<=(n);i++)36 37 #define for1(i,n) for(int i=1;i<=(n);i++)38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++)40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--)42 43 #define mod 100000000744 45 using namespace std;46 47 inline int read()48 49 {50 51     int x=0,f=1;char ch=getchar();52 53     while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}54 55     while(ch>=0&&ch<=9){x=10*x+ch-0;ch=getchar();}56 57     return x*f;58 59 }60 struct rec1{int x,y,z;}a[20],b[20];61 struct rec2{int x,y,z,w;}q[maxn];62 int n;63 64 int main()65 66 {67 68     freopen("input.txt","r",stdin);69 70     freopen("output.txt","w",stdout);71 72     n=read();73     for1(i,n)a[i].x=read();74     for1(i,n)a[i].y=read();75     for1(i,n)b[i].x=read();76     for1(i,n)b[i].y=read();77     for1(i,n)b[i].z=read();78     int l=0,r=0;79     q[++r].x=read(),q[r].y=read(),q[r].z=read(),q[r].w=0;80     int ans=q[r].x+q[r].y+q[r].z;81     while(l!=r)82     {83         rec2 now=q[++l];if(l==maxn)break;    84         for1(i,n)85          if((((now.w)&(1<<(i-1)))==0)&&(max(a[i].x-now.x,0)+max(a[i].y-now.y,0)<=now.z))86          {87           q[++r].x=max(now.x-a[i].x,0)+b[i].x;if(r==maxn)r=0;88           q[r].y=max(now.y-a[i].y,0)+b[i].y;89           q[r].z=now.z-max(a[i].x-now.x,0)-max(a[i].y-now.y,0)+b[i].z;90           q[r].w=now.w+(1<<(i-1));91           if(q[r].x+q[r].y+q[r].z>ans)ans=q[r].x+q[r].y+q[r].z;92          }93     }     94     printf("%d\n",ans);95 96     return 0;97 98 }
View Code

代码2:只以经过的门的集合为状态

 1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 #define N 50000 + 5 8  9 int n, x, y, z, ans, door[21][2], key[21][3];10 bool Open[N], Vis[N];11 12 inline void dfs(int state, int r, int g, int w)13 {14     if (Vis[state]) return ;15     ans = max(ans, r + g + w);16     for (int i = 1; i <= n; i ++)17         if (!Open[i])18         {19             int _r = r - door[i][0];20             int _g = g - door[i][1];21             int _w = w + min(_r, 0) + min(_g, 0);22             if (_w < 0) continue ;23             _r = max(_r, 0) + key[i][0];24             _g = max(_g, 0) + key[i][1];25             _w += key[i][2];26             Open[i] = 1;27             dfs(state ^ (1 << i - 1), max(_r, 0), max(_g, 0), _w);28             Open[i] = 0;29         }30     Vis[state] = 1;31 }32 33 int main()34 {35     scanf("%d", &n);36     for (int i = 0; i < 2; i ++)37         for (int j = 1; j <= n; j ++)38             scanf("%d", door[j] + i);39     for (int i = 0; i < 3; i ++)40         for (int j = 1; j <= n; j ++)41             scanf("%d", key[j] + i);42     scanf("%d%d%d", &x, &y, &z);43     dfs(0, x, y, z);44     printf("%d\n", ans);45     return 0;46 }
View Code

刚才orz了lyd的代码:发现他是这样做的状态:

f[i][j]表示经过门的集合为i,红钥匙数为 j 时白钥匙最多有多少,因为我们固定了红钥匙,所以白钥匙的数量越多越好。

因为它既可以当红的,也可以当蓝的使,白钥匙最多的时候一定可以打开所有白钥匙不是最多的时候能打开的门,而且还能打开一些原来打不开的门。见上面的例子。

这是一种贪心的思想。

然后,状态的转移就变的十分简单了,简直不能再orz。

代码3:lyd的神代码

 1 #include<cstring> 2 #include<cstdio> 3 #include<iostream> 4 #define fo(i,n) for(int i=0;i<n;i++) 5 using namespace std; 6 const int N=15; 7 int dR[N],dG[N],rR[N],rG[N],rW[N],ky[N],n,f[20000][200],z; 8 int main(){ 9     freopen("input.txt", "r",stdin);10     freopen("output.txt","w",stdout);11     cin>>n;12     fo(i,n) cin>>dR[i];13     fo(i,n) cin>>dG[i];14     fo(i,n) cin>>rR[i];15     fo(i,n) cin>>rG[i];16     fo(i,n) cin>>rW[i];17     cin>>ky[0]>>ky[1]>>ky[2];18     int sum=ky[0]+ky[1]+ky[2];19     memset(f,-1,sizeof f);20     f[0][ky[0]]=ky[2];21     fo(i,1<<n){22         int k0=ky[0],k1=ky[1],r=sum;23         fo(j,n)24             if(i>>j&1){25                 k0+=rR[j];26                 r+=rR[j]+rG[j]+rW[j]-dR[j]-dG[j];27             }28         for(int j=0;j<=k0;j++){29             if(f[i][j]==-1) continue;30             int fr=f[i][j];31             int k=r-fr-j;32             fo(l,n){33                 if(i>>l&1) continue;34                 int r=max(0,dR[l]-j),g=max(0,dG[l]-k);35                 int &q=f[i|(1<<l)][max(0,j-dR[l])+rR[l]];36                 if(fr>=r+g)37                     q=max(q,fr-r-g+rW[l]);38             }39             z=max(z,j+k+fr);40         }41     }42     cout<<z<<endl;43 }
View Code

 

CH Round #53 -密室