HDOJ 4285 circuits (插头dp 总结)

本文介绍了一种解决特定棋盘问题的方法,该问题要求计算包含特定数量环的方案数。采用动态规划与哈希优化相结合的方式进行高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给出一个有障碍棋牌,让我们算出有多少种包含有k个环的方案数,其中每个非障碍棋盘必须包含在一个环中,并且不能有换套环的情况(这个条件没看到,花了几个小时调试,坑的一逼呀!!!!!)


行为n,列为m,环数k


插头dp:以列j为内循环,以行i为外循环,每次加入一个节点,那么分割线上就有m+1个小方格边,以这些边的状态计算下次的状态形成状态转移,因为环不能相交所以括号原理就刚好用得上了


注意,有环数要求,我们就加一个环数的标记位,而换套环是约束条件是,每次形成环的时候左右两边的插头数都为偶数才行,因为左右两边的插头是偶数,那么只要有一次左右两边的插头相连接形成环时,这时候左右两边的插头数就不为偶数了;


内存也限制得很厉害,我是先把内存开很大,结果算出最大的状态数发现才420000,就解决了内存的问题;

因为状态数量太多不能用纯粹的dp来做,我是用hash优化的;

还要注意位运算要比%快得多,所以虽然每个插头只有3个状态,但是我们用两个位来记录会快很多;

环的最大数量是12 * 12 / 4 = 36 要六位,而状态有(12 + 1) * 2 = 26位,刚好int型可以表示,但注意位运算向低位移动的时候负数会在最后一位补1,所以很坑的又调试len很长时间


这道题我提交了32次才过,真是功夫不负有心人哪,加油!!


我的代码:


#include<string.h>
#include<math.h>
#include<stdio.h>
#include<algorithm>
using namespace std;


#define MAX 1333317


int que[2][420000], last[2], flag;
int num[2][420000];


int mmax;


int index;


int hash[MAX], list[MAX], where[MAX];


char map[20][20];
int n, m, k;


int find(int x, int la)
{
int xx = x % MAX;
while(xx < 0)
xx += MAX;
while(hash[xx] == index && list[xx] != x)
xx = (xx + 1) % MAX;
if(hash[xx] != index)
{
where[xx] = la;
list[xx] = x;
hash[xx] = index;
return 0;
}
return where[xx];
}


int getarray(int a, int j)
{
if(j <= m)
return (a >> (j * 2)) & 3;
else
return (a >> (m + 1) * 2) & ((1 << 6) - 1);
}


int setarray(int *a, int j, int x)
{
if(j <= m)
{
*a = (*a) & (~(3 << j * 2));
*a += x << j * 2;
}
else
{
*a &= (1 << ((m + 1) * 2)) - 1;
*a |= x << (2 * m + 2);
}
return 0;
}




int getnext(int a, int j)
{
int f = 1;
for(int i = j + 1; i <= m; i ++)
{
if(getarray(a, i) == 1)
f ++;
if(getarray(a, i) == 2)
f --;
if(f == 0)
return i;
}
return 0;
}


int getfirst(int a, int j)
{
int f = 1;
for(int i = j - 1; i >= 0; i --)
{
if(getarray(a, i) == 2)
f ++;
if(getarray(a, i) == 1)
f --;
if(f == 0)
return i;
}
return 0;
}


int add(int nu, int a)
{
if(getarray(nu, m + 1) > k)
return 0;
int p = find(nu, last[1 - flag] + 1);
if(p != 0)
{
num[1 - flag][p] += num[flag][a];
num[1 - flag][p] %= 1000000007;
}
else
{
que[1 - flag][++ last[1 - flag]] = nu;
num[1 - flag][last[1 - flag]] = num[flag][a];
// if(last[1 - flag] > mmax)
// mmax = last[1 - flag];
}
return 0;
}


int check(int a, int j)
{
int i;
int x = 0;
for(i = j - 1; i >= 0; i --)
{
if(getarray(a, i) == 1)
x ++;
if(getarray(a, i) == 2)
x --;
}
return x % 2;
}


int change(int *nu)
{
int x = *nu;
setarray(nu, 0, 0);
for(int i = 1; i <= m; i ++)
setarray(nu, i, getarray(x, i - 1));
return 0;
}


__int64 getResult()
{
int i, j, z;


int a, b, nu;

flag = 0;
last[flag] = 1;
que[flag][1] = 0;
num[flag][1] = 1;



for(i = 0; i < n; i ++)
for(j = 0; j < m; j ++)
{
if(j == 0)
{
last[1 - flag] = 0;
for(z = 1; z <= last[flag]; z ++)
{
nu = que[flag][z];
if(getarray(nu, m) != 0)
continue;
change(&nu);
que[1 - flag][++ last[1 - flag]] = nu;
num[1 - flag][last[1 - flag]] = num[flag][z];
}
flag = 1 - flag;
}
last[1 - flag] = 0;

if(map[i][j] == '.')
{
index --;
for(z = 1; z <= last[flag]; z ++)
{
nu = que[flag][z];
a = getarray(nu, j);
b = getarray(nu, j + 1);
if(a == b && a != 0 )
{
if(a == 1)
{
setarray(&nu, getnext(nu, j + 1), 1);
// array[getnext(array, j + 1)] = 1;
}
else
{
setarray(&nu, getfirst(nu, j), 2);
// array[getfirst(array, j)] = 2;
}
setarray(&nu, j, 0);
setarray(&nu, j + 1, 0);
add(nu, z);
}
else if(a == 2 && b == 1)
{
setarray(&nu, j, 0);
setarray(&nu, j + 1, 0);
add(nu, z);

}
else if(a == 1 && b == 2)
{
if(check(nu, j))
continue;
setarray(&nu, j, 0);
setarray(&nu, j + 1, 0);
setarray(&nu, m + 1, getarray(nu, m + 1) + 1);
add(nu, z);
}
else if(a == 0 && b == 0)
{
setarray(&nu, j, 1);
setarray(&nu, j + 1, 2);
add(nu, z);
}
else
{
add(nu, z);

setarray(&nu, j, getarray(que[flag][z], j + 1));
setarray(&nu, j + 1, getarray(que[flag][z], j));
add(nu, z);
}
}


}
else
{
for(z = 1; z <= last[flag]; z ++)
{
nu = que[flag][z];
if(getarray(nu, j) || getarray(nu, j + 1))
continue;
que[1 - flag][++ last[1 - flag]] = que[flag][z];
num[1 - flag][last[1 - flag]] = num[flag][z];
}
}
flag = 1 - flag;
}
for(i = 1; i <= last[flag]; i ++)
{
nu = que[flag][i];
for(j = 0; j <= m; j ++)
{
if(getarray(nu, j) != 0)
break;
if(j == m && getarray(nu, m + 1) == k)
return num[flag][i];
}
}
return 0;
}


int main()
{
// freopen("in.txt","r",stdin);
  //  freopen("out.txt","w",stdout);
int i, j;
int px;
int ca,cases;
scanf("%d", &cases);
for(ca = 1; ca <= cases; ca ++)
{
// mmax = 0;
px = 0;
scanf("%d%d%d", &n, &m, &k);
memset(hash, 0, sizeof(hash));

for(i = 0; i < n; i ++)
{
scanf("%s", map[i]);
for(j = 0; j < m; j ++)
if(map[i][j] == '*')
px ++;
}
if(k > (m * n - px) / 4)
{
printf("0\n");
continue;
}
index = 0;
printf("%I64d\n", getResult());
// printf("%d\n", mmax);
}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值