0
点赞
收藏
分享

微信扫一扫

ural 1519

TiaNa_na 2022-08-08 阅读 30


​​http://www.elijahqi.win/2017/07/07/ural-1519/​​​
网上说这题作为插头dp的入门题我觉得还是难了一些

  1. Formula 1
    Time Limit: 1.0 second
    Memory Limit: 16 MB
    Background
    Regardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well-known, that the city will conduct one of the Formula 1 events. Surely, for such an important thing a new race circuit should be built as well as hotels, restaurants, international airport - everything for Formula 1 fans, who will flood the city soon. But when all the hotels and a half of the restaurants were built, it appeared, that at the site for the future circuit a lot of gophers lived in their holes. Since we like animals very much, ecologists will never allow to build the race circuit over the holes. So now the mayor is sitting sadly in his office and looking at the map of the circuit with all the holes plotted on it.
    Problem
    Who will be smart enough to draw a plan of the circuit and keep the city from inevitable disgrace? Of course, only true professionals - battle-hardened programmers from the first team of local technical university!.. But our heroes were not looking for easy life and set much more difficult problem: “Certainly, our mayor will be glad, if we find how many ways of building the circuit are there!” - they said.
    It should be said, that the circuit in Vologda is going to be rather simple. It will be a rectangle N*M cells in size with a single circuit segment built through each cell. Each segment should be parallel to one of rectangle’s sides, so only right-angled bends may be on the circuit. At the picture below two samples are given for N = M = 4 (gray squares mean gopher holes, and the bold black line means the race circuit). There are no other ways to build the circuit here.
    Input
    The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character “.” (full stop) means a cell, where a segment of the race circuit should be built, and character “*” (asterisk) - a cell, where a gopher hole is located.
    Output
    You should output the desired number of ways. It is guaranteed, that it does not exceed 263-1.

题目简意:
给你一个m * n的棋盘,有的格子是障碍,问共有多少条回路使得经过每个非障碍格子恰好一次.m, n ≤ 12。

如图,m = n = 4,(1, 1), (1, 2)是障碍,共有2条满足要求的回路.
首先读入数据
读入整个棋盘 找出最后一个可经过的点

for (int i=0;i<n;++i) {
scanf("%s",map[i]);
for (int j=0;j<m;++j) if (map[i][j]=='.') ex=x,ey=y;
}

建立hash 表 储存所有状态
1 确定一个数N1 N1即为有几层hash 表
每层hash 有多少取决于状态所分层数的数量的多少 head一开始均指向-1
类似邻接表 head指向最后一层 然后next分别是这一层指向下一层
head[0] head[1] next
层数 -1 -1
1
2
3
4
5

void init(){
size=0;memset(head,-1,sizeof(head));
}
void insert1(int st,long long value){
int tmp=st%N1;
for(int i=head[tmp];i!=-1;i=next[i];){
if(state[i]==st){
s[i]+=value;return;
}
state[size]=st;s[size]=value;next[size]=head[tmp];head[tmp]=size++;
}

}

初始化最初的扫描线
设定 idx 与cur 分别为当前状态和上次的状态
由于使用括号表示法 1表示左括号 2表示右括号 故三进制储存状态即可 但鉴于计算机对于位运算是飞速的 所以我们一般采用 二进制或者其倍数倍的进制
在每行结束后需要将四进制的状态 左移一次

如当前状态 四进制表示法 10122 (真正的储存应该是倒序)【22101】
整体左移一位221010 反向表示 010122 相当于将扫描线推到0开始
__
_|
扫描线:0 1 0 1 2 2
初始化

hash[0].init();hash[0].size=1;
hash[0].state[0]=0;hash[0].s[0]=1;idx=0;
从第一格开始逐格递推
for (int i=0;i<n;++i){
int size1=hash[idx].size;
for (int j=0;j<size1;++j) hash[idx].state[j]<<2;//需要将上一行每个状态都要转移
for (int j=0;j<m;++j){
size1=hash[idx].size;
cur=idx^1;
hash[cur].init();
for (int z=0;z<size1;++z) updata(i,j,hash[idx].state[z],hash[idx].s[z]);
}
}

转移过程 updata
在写 updata之前 我们需要知道插头一共有几种状态

由于这是哈密顿回路 所以每个点只经过一次所以插头满足上述六种情况
从图中的任意一点出发,路途中经过图中每一个结点当且仅当一次,则成为哈密顿回路。
要满足两个条件: ⒈封闭的环 ⒉是一个连通图,且图中任意两点可达

取出当前状态 的p与q p=get(st,y);q=get(st,y+1);需要由p,q推导出 p​​,q​

if (p==0&&q==0){
if (x==n-1||y==m-1) return;//如果当前位置处于行末或者行尾 不能再放置插头否则不能形成闭合回路
st=set(st,y,1);
st=set(st,y+1,2);
hash[cur].insert1(st,value);//继承上一行状态的数量
return;
}
if (p==0||q==0){}

if (y<m-1){
st=sets(st,y,0);
st=sets(st,y+1,p+q);
hash[cur].insert1(st,value);
}


if (x<n-1){
int newstate=st;
newstate=sets(st,y,p+q);
newstate=sets(newstate,y+1,0);
hash[cur].insert1(newstate,value);
}


if (p==1&&q==1) {
newstate=sets(newstate,getr(st,y+1),1);
//给y+1这个匹配的2号插头改为一号插头
hash[cur].insert1(newstate,value);
return;
}


if (p==1&&q==1) {
newstate=sets(newstate,getr(st,y+1),1);
//给y+1这个匹配的2号插头改为一号插头
hash[cur].insert1(newstate,value);
return;
}

只有当这是最有一个 左插头1 右插头2的状态才可以将两种状态合并

if (p==1&&q==2) {
if (x==ex&&y==ey) hash[cur].insert1(newstate,value);else return;
}

FULL CODE:

#include<cstdio>
#include<cstring>
#define
#define
#define
char map[N][N];
int parenthese[4]={0,-1,1,0};
int n,m,ex,ey;
struct node{
int head[N2];
int size;//hash值的层数
int state[N1];
int next[N1];
long long s[N1];
void init(){
size=0;memset(head,-1,sizeof(head));
}
void insert1(int st,long long value){
int h=st%N2;
for (int i=head[h];i!=-1;i=next[i]){
if (state[i]==st){
s[i]+=value;return;
}
}
state[size]=st;s[size]=value;
next[size]=head[h];head[h]=size++;
}
long long answer(int st){
int h=st%N2;
for (int i=head[h];i!=-1;i=next[i]) if (state[i]==st) return s[i];
return 0;
}
}hash[2];
int idx,cur;
int sets(int st,int x,int v){
x<<=1;
return (st& ~(3<<x))|(v<<x);
}
int get(int st,int x){
x<<=1;//将x由四进制位数转换为二进制的位数
return(st>>x)&3; //将st四进制的末尾以(二进制的两位)与3按位& 返回
}
int getl(int st,int x){
int l=x,cnt=1;
while (cnt) cnt+=parenthese[get(st,--l)];
return l;
}
int getr(int st,int x){
int r=x,cnt=-1;
while (cnt) cnt+=parenthese[get(st,++r)];
return r;
}
void updata(int x,int y,int st,long long value){
int p=get(st,y),q=get(st,y+1);
if (map[x][y]=='*'){
if (p==0&&q==0) hash[cur].insert1(st,value);
return ;
}
if (p==0&&q==0){
if (x==n-1||y==m-1) return;//到行尾或者列尾不能再放置插头 否则不能形成闭合回路
st=sets(st,y,1);
st=sets(st,y+1,2);
hash[cur].insert1(st,value);//继承上一行状态的数量
return;
}
if (p==0||q==0){
if (x<n-1){
int newstate=st;
newstate=sets(st,y,p+q);
newstate=sets(newstate,y+1,0);
hash[cur].insert1(newstate,value);
}
if (y<m-1){
st=sets(st,y,0);
st=sets(st,y+1,p+q);
hash[cur].insert1(st,value);
}
return;
}
int newstate=st;
newstate=sets(st,y,0);newstate=sets(newstate,y+1,0);//首先将状态清0
if (p==1&&q==1) {
newstate=sets(newstate,getr(st,y+1),1);
//给y+1这个匹配的2号插头改为一号插头
hash[cur].insert1(newstate,value);
return;
}
if (p==1&&q==2) {
if (x==ex&&y==ey) hash[cur].insert1(newstate,value);else return;
}
if (p==2&&q==2) {
newstate=sets(newstate,getl(st,y),2);
hash[cur].insert1(newstate,value);
return;
}
if (p==2&&q==1) hash[cur].insert1(newstate,value);
}
int main(){
freopen("ural1519.in","r",stdin);
freopen("ural1519.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=0;i<n;++i) scanf("%s",map[i]);
for (int i=0;i<n;++i)
for (int j=0;j<m;++j) if (map[i][j]=='.') ex=i,ey=j;
//printf("%d %d",ex,ey);
hash[0].init();
hash[0].size=1;hash[0].state[0]=0;
hash[0].s[0]=1;idx=0;
for (int i=0;i<n;++i){
int size1=hash[idx].size;
for (int j=0;j<size1;++j) hash[idx].state[j]<<=2;
for (int j=0;j<m;++j){
size1=hash[idx].size;
cur=idx^1;
hash[cur].init();
for (int z=0;z<size1;++z) updata(i,j,hash[idx].state[z],hash[idx].s[z]);
idx=cur;
}
// for (int i=0;i<hash[idx].size;++i) printf("%d %d %d\n",hash[idx].state[i],hash[idx].s[i],hash[idx].size);
}

printf("%lld",hash[cur].answer(0));
return 0;
}


举报

相关推荐

0 条评论