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,依次表示初始你拥有的红色、绿色、白色钥匙的数量。
输出格式
输出一个整数,表示你最多能有几把钥匙。
样例输入
3
1 2 3
0 4 9
0 0 10
0 8 9
1 0 8
3 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
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<string>
#define inf 1000000000
#define maxn 10000000
#define maxm 500+100
#define eps 1e-10
#define ll long long
#define pa pair<int,int>
#define for0(i,n) for(int i=0;i<=(n);i++)
#define for1(i,n) for(int i=1;i<=(n);i++)
#define for2(i,x,y) for(int i=(x);i<=(y);i++)
#define for3(i,x,y) for(int i=(x);i>=(y);i--)
#define mod 1000000007
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=*x+ch-'';ch=getchar();}
return x*f;
}
struct rec1{int x,y,z;}a[],b[];
struct rec2{int x,y,z,w;}q[maxn];
int n;
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
n=read();
for1(i,n)a[i].x=read();
for1(i,n)a[i].y=read();
for1(i,n)b[i].x=read();
for1(i,n)b[i].y=read();
for1(i,n)b[i].z=read();
int l=,r=;
q[++r].x=read(),q[r].y=read(),q[r].z=read(),q[r].w=;
int ans=q[r].x+q[r].y+q[r].z;
while(l!=r)
{
rec2 now=q[++l];if(l==maxn)break;
for1(i,n)
if((((now.w)&(<<(i-)))==)&&(max(a[i].x-now.x,)+max(a[i].y-now.y,)<=now.z))
{
q[++r].x=max(now.x-a[i].x,)+b[i].x;if(r==maxn)r=;
q[r].y=max(now.y-a[i].y,)+b[i].y;
q[r].z=now.z-max(a[i].x-now.x,)-max(a[i].y-now.y,)+b[i].z;
q[r].w=now.w+(<<(i-));
if(q[r].x+q[r].y+q[r].z>ans)ans=q[r].x+q[r].y+q[r].z;
}
}
printf("%d\n",ans);
return ;
}
代码2:只以经过的门的集合为状态
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 50000 + 5 int n, x, y, z, ans, door[][], key[][];
bool Open[N], Vis[N]; inline void dfs(int state, int r, int g, int w)
{
if (Vis[state]) return ;
ans = max(ans, r + g + w);
for (int i = ; i <= n; i ++)
if (!Open[i])
{
int _r = r - door[i][];
int _g = g - door[i][];
int _w = w + min(_r, ) + min(_g, );
if (_w < ) continue ;
_r = max(_r, ) + key[i][];
_g = max(_g, ) + key[i][];
_w += key[i][];
Open[i] = ;
dfs(state ^ ( << i - ), max(_r, ), max(_g, ), _w);
Open[i] = ;
}
Vis[state] = ;
} int main()
{
scanf("%d", &n);
for (int i = ; i < ; i ++)
for (int j = ; j <= n; j ++)
scanf("%d", door[j] + i);
for (int i = ; i < ; i ++)
for (int j = ; j <= n; j ++)
scanf("%d", key[j] + i);
scanf("%d%d%d", &x, &y, &z);
dfs(, x, y, z);
printf("%d\n", ans);
return ;
}
刚才orz了lyd的代码:发现他是这样做的状态:
f[i][j]表示经过门的集合为i,红钥匙数为 j 时白钥匙最多有多少,因为我们固定了红钥匙,所以白钥匙的数量越多越好。
因为它既可以当红的,也可以当蓝的使,白钥匙最多的时候一定可以打开所有白钥匙不是最多的时候能打开的门,而且还能打开一些原来打不开的门。见上面的例子。
这是一种贪心的思想。
然后,状态的转移就变的十分简单了,简直不能再orz。
代码3:lyd的神代码
#include<cstring>
#include<cstdio>
#include<iostream>
#define fo(i,n) for(int i=0;i<n;i++)
using namespace std;
const int N=;
int dR[N],dG[N],rR[N],rG[N],rW[N],ky[N],n,f[][],z;
int main(){
freopen("input.txt", "r",stdin);
freopen("output.txt","w",stdout);
cin>>n;
fo(i,n) cin>>dR[i];
fo(i,n) cin>>dG[i];
fo(i,n) cin>>rR[i];
fo(i,n) cin>>rG[i];
fo(i,n) cin>>rW[i];
cin>>ky[]>>ky[]>>ky[];
int sum=ky[]+ky[]+ky[];
memset(f,-,sizeof f);
f[][ky[]]=ky[];
fo(i,<<n){
int k0=ky[],k1=ky[],r=sum;
fo(j,n)
if(i>>j&){
k0+=rR[j];
r+=rR[j]+rG[j]+rW[j]-dR[j]-dG[j];
}
for(int j=;j<=k0;j++){
if(f[i][j]==-) continue;
int fr=f[i][j];
int k=r-fr-j;
fo(l,n){
if(i>>l&) continue;
int r=max(,dR[l]-j),g=max(,dG[l]-k);
int &q=f[i|(<<l)][max(,j-dR[l])+rR[l]];
if(fr>=r+g)
q=max(q,fr-r-g+rW[l]);
}
z=max(z,j+k+fr);
}
}
cout<<z<<endl;
}
CH Round #53 -密室的更多相关文章
- CH Round #53 -【Nescafé 32】杯NOIP模拟赛
A.GCD Path http://ch.ezoj.tk/contest/CH%20Round%20%2353%20-%E3%80%90Nescaf%C3%A9%2032%E3%80%91%E6%9D ...
- CH Round #53 -GCD Path
描述 给定一张N个点的有向图,点i到点j有一条长度为 i/(gcd(i,j))的边.有Q个询问,每个询问包含两个数x和y,求x到y的最短距离. 输入格式 第一行包含两个用空格隔开的整数,N和Q. 接下 ...
- Educational Codeforces Round 53 (Rated for Div. 2) (前五题题解)
这场比赛没有打,后来补了一下,第五题数位dp好不容易才搞出来(我太菜啊). 比赛传送门:http://codeforces.com/contest/1073 A. Diverse Substring ...
- CH Round #52 还教室[线段树 方差]
还教室 CH Round #52 - Thinking Bear #1 (NOIP模拟赛) [引子]还记得 NOIP 2012 提高组 Day2 中的借教室吗?时光飞逝,光阴荏苒,两年过去了,曾经借教 ...
- CH Round #72树洞[二分答案 DFS&&BFS]
树洞 CH Round #72 - NOIP夏季划水赛 描述 在一片栖息地上有N棵树,每棵树下住着一只兔子,有M条路径连接这些树.更特殊地是,只有一棵树有3条或更多的路径与它相连,其它的树只有1条或2 ...
- CH Round #30 摆花[矩阵乘法]
摆花 CH Round #30 - 清明欢乐赛 背景及描述 艺术馆门前将摆出许多花,一共有n个位置排成一排,每个位置可以摆花也可以不摆花.有些花如果摆在相邻的位置(隔着一个空的位置不算相邻),就不好看 ...
- contesthunter CH Round #64 - MFOI杯水题欢乐赛day1 solve
http://www.contesthunter.org/contest/CH Round %2364 - MFOI杯水题欢乐赛 day1/Solve Solve CH Round #64 - MFO ...
- CH Round #17 舞动的夜晚
舞动的夜晚 CH Round #17 描述 L公司和H公司举办了一次联谊晚会.晚会上,L公司的N位员工和H公司的M位员工打算进行一场交际舞.在这些领导中,一些L公司的员工和H公司的员工之间是互相认识的 ...
- CH Round #45 能量释放
能量释放 CH Round #45 - alan有一些陷阱 III 题目描述 alan得到一块由个能量晶体构成的矿石,对于矿石中的每一个能量晶体,如果用化学物质刺激某一个能量晶体,就能使它释放能量. ...
随机推荐
- 如何断开eclipse与svn的链接
右键点击需要断开的项目 Team-->Disconnect 如果想删除svn配置文件,选择Also delete the SVN meta information from the file s ...
- POJ 3723 Conscription
Conscription Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6325 Accepted: 2184 Desc ...
- Ubuntu中apt-get出现E:Encountered a section with no Package: header……的解决方案
方法一:运行命令apt-get update更新list列表 方法二:将/var/lib/apt/lists/下的所有list文件都删除,然后再update
- Oracle_Q&A_01
Step01: SHOW USER DESCRIPTION Step02:QUESTION & ANSWER --查询员工姓名和职位字数相等的员工 SELECT * from emp wher ...
- [深入React] 5.MVC
react 是一种典型的MVC框架. 在jquery中,我们都是针对dom编程,由dom事件触发dom改变,MVC三层代码混在一起. 比如点击变颜色 $('#btn').click(function( ...
- json对象转字符串与json字符串转对象
1.概述: 我们在编程时进场会遇到json对象转字符串,或者字符串转对象的情况. 2.解决办法: json.parse()方法是将json字符串转成json对象. json.stringfy()方法是 ...
- MFC原创:三层架构01(人事管理系统)DAL
VC++/MFC Window编程原创教程文件夹 C++课程设计来着.但还没学过数据,也还没理解过三层架构,就把这个作业深化点来做了.尽管要做的这个人事管理系统看起来是挺简单的,无非就是处理员工信息. ...
- Java为什么使用连接池
一.简介 动态Web站点往往用数据库存储的信息生成Web页面,每一个页面请求导致一次数据库访问.连接数据库不仅要开销一定的通信和内存资源,还必须完成用户验证.安全上下文配置这类任务,因为往往成为最为耗 ...
- Mac下Qt连接MySQL 驱动问题
Mac OS X下Qt的mySQL driver编译安装 原创文章,采用CC协议发布,转载请注明: 转载自canX.me 本文链接地址: Mac OS X下Qt的mySQL driver编译安装 – ...
- 你的sscanf用对了吗
用sscanf解析输入字符串 我们平常编写的很多应用程序都会处理各种各样的输入,这些输入或来自本地文件,或来自网络,或来自用户的输入.今天,让我们来看看sscanf这个和字符串相关的函数可能给你带来的 ...