您的位置:首页 > 其它

URAL 1519 Formula 1

2015-10-22 19:07 288 查看
题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1519

陈丹琦的《基于连通性状态压缩的动态规划问题》的论文上的题目

题意:

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

做法:

论文上的思路讲的很清楚了,这里用最小表示法来做。

因为(2 ≤ N, M ≤ 12),所以最多出现6个联通块,所以用8进制来做。

#include <bits/stdc++.h>
using namespace std;
int N, M;
#define maxn 15
#define HASH 30007
#define STATE 200010
int mp[maxn][maxn];
int pos[maxn];
int mark, ex, ey;
int code[maxn];
struct HashMap
{
int head[HASH], next[STATE], cnt;
long long state[STATE];
long long sum[STATE];
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void push(long long sta, long long val)
{
int h = sta%HASH;
for(int i = head[h]; i != -1; i = next[i])
{
if(state[i] == sta)  //如果这种状态已经存在
{
sum[i] += val;
return;
}
}
state[cnt] = sta;
sum[cnt] = val;
next[cnt] = head[h];
head[h] = cnt++;
}
}hashmp[2];//滚动数组
void decode(int *code, long long sta)
{
for(int i = M; i >= 0; i--)
{
code[i] = sta&7;
sta >>= 3;
}
}
long long encode(int *code) //转化成2进制
{
int cnt = 1;
int vis[maxn];
memset(vis, -1, sizeof(vis));
vis[0] = 0;  //0不连通
long long sta = 0;

for(int i = 0; i <= M; i++)
{
if(vis[code[i]] == -1) vis[code[i]] = cnt++;
code[i] = vis[code[i]];
sta <<= 3;
sta |= code[i];
}
return sta;
}
void AtLast(int *code)
{
for(int i = M; i > 0; i--) code[i] = code[i-1];
code[0] = 0;
}
void slove()
{
mark = 0;
hashmp[mark].init();
hashmp[mark].push(0, 1);       //开始时轮廓线在最上方

for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= M; j++)
{
hashmp[mark^1].init();//初始化
if(mp[i][j] == 0)     //不能放置
{
for(int k = 0; k < hashmp[mark].cnt; k++)
{
decode(code, hashmp[mark].state[k]);
code[j-1] = code[j] = 0;
if(j == M) AtLast(code);
hashmp[mark^1].push(encode(code),hashmp[mark].sum[k]);
}
}
else                //可以放置
{
for(int k = 0; k < hashmp[mark].cnt; k++)
{
decode(code, hashmp[mark].state[k]);
int left, up;
left = code[j-1]; up = code[j];
if(left == 0 && up == 0)            //情况1:没有上插头和左插头,新建一个联通分量
{
if(mp[i+1][j] && mp[i][j+1])
{
code[j-1] = code[j] = 13;  //如果mp[i][j+1] = 1,则不可能出现j == M的情况
hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
}
}
else if(left && up)                //情况2:有上插头和左插头
{
if(left != up)                 //上插头和左插头不联通,合并联通分量。
{
code[j-1] = code[j] = 0;   //则不可能再有右插头和下插头
for(int ii = 0; ii <= M; ii++)
{
if(code[ii] == up) code[ii] = left;
}
if(j == M) AtLast(code);
hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
}
else if(left == up)            //上插头和左插头联通
{

if(i == ex && j == ey)     //这种情况只可能出现在最后一个可以摆放的位置
{
code[j-1] = code[j] = 0;
if(j == M) AtLast(code);
hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
}
}
}
else if((left&&(!up)) || (up&&(!left)))//情况3:上插头和左插头只存在一个
{
int val;
if(left) val = left;
else val = up;
if(mp[i][j+1])     //右边的格子可走
{
code[j] = val; code[j-1] = 0;
hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
}
if(mp[i+1][j])    //下面的格子可走
{
code[j-1] = val; code[j] = 0;
if(j == M) AtLast(code);
hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]);
}
}
}
}
mark^=1;
}
}
}
int main()
{
// freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
while(~scanf("%d%d", &N, &M))
{
memset(mp, 0, sizeof(mp));
bool flag = false;
for(int i = 1; i <= N; i++)
{
char str[15];
scanf("%s", str);
for(int j = 0; j < M; j++)
{
if(str[j] == '.')
{
mp[i][j+1] = 1;
ex = i; ey = j+1;
flag = true;
}
else if(str[j] == '*') mp[i][j+1] = 0;
}
}
if(flag == false)  //没有空块
{
printf("0\n"); continue;
}
slove();
long long ans = 0;;
for(int i = 0; i < hashmp[mark].cnt; i++)
{
ans += hashmp[mark].sum[i];
}
printf("%lld\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: