SCOI 2009 围豆豆

题目描述

是不是平时在手机里玩吃豆豆游戏玩腻了呢?最近MOKIA手机上推出了一种新的围豆豆游戏,大家一起来试一试吧。

游戏的规则非常简单,在一个N×M的矩阵方格内分布着D颗豆子,每颗豆有不同的分值Vi。游戏者可以选择任意一个方格作为起始格,每次移动可以随意的走到相邻的四个格子,直到最终又回到起始格。最终游戏者的得分为所有被路径围住的豆豆的分值总和减去游戏者移动的步数。矩阵中某些格子内设有障碍物,任何时刻游戏者不能进入包含障碍物或豆子的格子。游戏者可能的最低得分为0,即什么都不做。

注意路径包围的概念,即某一颗豆在路径所形成的多边形(可能是含自交的复杂多边形)的内部。

输入格式

第一行两个整数N和M,为矩阵的边长。

第二行一个整数D,为豆子的总个数。

第三行包含D个整数V1到VD,分别为每颗豆子的分值。

接着N行有一个N×M的字符矩阵来描述游戏矩阵状态,0表示空格,#表示障碍物。而数字1到9分别表示对应编号的豆子。

输出格式

仅包含一个整数,为最高可能获得的分值

Sample

Input

3 8
3
30 -100 30
00000000
010203#0
00000000

Output

38

Solution

令人头大的一道题

定义数组:\(f[i][j][k]\)表示当前所在点为\(i,j\)走过了\(k\)步

k就是状压中的“状态”

怎么理解k呢

每一位i表示第i颗豆子有没有被围上

0表示没有,1表示围上了

假设有3颗豆子

000都没有

001一号围上了

011一二号围上了

100三号围上了

以此类推

然后就是判断有没有被围上的方法

这个东西属实不知道

是个叫做射线定理的东西

我们从豆豆开始向右引一条射线(其实向哪一个方向都可以),如果射线与路径的交点为奇数个,那么豆豆能被围住,反之则不能

如上

证明:

给每一条线规定内外

那么每穿过一次就反转了多边形的内外

若穿过奇数次,证明又回来了,所以在内部

反之在外部

代码参考网上

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int q[11][11][1050], f[11][11], x[11], y[11], v[11], ans;
int n, m, d, dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1}; struct haha{
int a, b, c;
}dou; int gc(int a,int b){
char t = ' ';
while(1){
scanf("%c",&t);
if (t == '#') return -1;
if (t >= '0' && t <= '9'){
x[t - '0'] = a;
y[t - '0'] = b;
return t - '0';
}
}
} queue<haha>p;
int in[11][11][1050]; inline void doit(int stx, int sty){
memset(q, 0,sizeof(q ));
memset(in, 0, sizeof(in));
q[stx][sty][0] = 0;
p.push({stx, sty, 0});
int tx,ty,tz,tq,cross;
while (!p.empty()){
dou=p.front();p.pop();
for (int dir=0;dir<4;dir++){
tx=dou.a+dx[dir];ty=dou.b+dy[dir];
if (tx<=0||ty<=0||tx>n||ty>m||f[tx][ty]!=0) continue;
tz=dou.c;tq=q[dou.a][dou.b][dou.c]-1;
if (dir<2){
cross=min(tx,dou.a);
for (int i=1;i<=d;i++){
if (x[i]!=cross||y[i]>ty) continue;
if (tz&(1<<(i-1)))
tq-=v[i];
else
tq+=v[i];
tz=tz^(1<<(i-1));
}
}
if (!in[tx][ty][tz]){
q[tx][ty][tz]=tq;
p.push({tx,ty,tz});
in[tx][ty][tz]=1;
}
}
}
for (int i=0;i<(1<<d);i++)
if (in[stx][sty][i])
ans=max(ans,q[stx][sty][i]);
} int main(){
scanf("%d%d",&n,&m);
scanf("%d",&d);
for (int i=1;i<=d;i++)
scanf("%d",&v[i]);
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
f[i][j]=gc(i,j);
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (f[i][j]==0)
doit(i,j);
printf("%d",ans);
}

最新文章

  1. Linux常用的命令以及一些常见的问题
  2. web页面全角&amp;半角
  3. 微信公众平台应用开发:方法、技巧与案例--柳峰,Java语言版本
  4. *IDEA真好用
  5. OpenVPN莫名其妙断线的问题及其解决-confirm
  6. ASP.NET- LinkButton 传递多个参数
  7. 保留n位四舍五入小数
  8. Bertelsmann Asia Investments, 简称BAI
  9. S3C6410 纯粹的裸机启动,自己写的SD BOOT启动
  10. storm serializable
  11. Laravel 中使用子域名(一个框架多项目)
  12. tomcat 日志切割 catalina.out
  13. web组件开发入门
  14. gradle入门(1-2)gradle的依赖管理
  15. ubuntu查看IO
  16. 用keras实现基本的回归问题
  17. oracle 添加登陆数据库触发器--记录IP 地址
  18. 伪类target实现纯css模态框
  19. Pandas分类
  20. NFS 网络文件系统制作

热门文章

  1. java启动RabbitMQ消息报异常解决办法
  2. 初学python笔记
  3. 用mvc框架查询数据库数据
  4. java实现简单的oss存储
  5. CenterOS7 修改 SSH 端口
  6. openshift v1.5 不能登录system:admin 问题
  7. [每日一题2020.06.13]leetcode #739 #15 单调栈 双指针查找
  8. GestureDetector手势检测
  9. 项目实战:Qt手机模拟器拉伸旋转框架
  10. 循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理