题目
Description
Bessie和她的朋友们被抓走并关在了远离农场的一个秘密房屋,现在该是Bessie站出来策划脱逃的时候了!这一房屋包含NK个排列成N×K矩形方阵的囚室,水平和垂直方向相邻的囚室之间有门互通。每个囚室中有一头奶牛。
Bessie黑进了系统,可以解锁任意一部分的门,但是每个门均有其代价。为了使奶牛们能够脱逃,Bessie必须打开足够多的门使得所有的奶牛可以聚集在一个房间内(这样她们就能拥有足够的力量挖地洞通向外面!)。Bessie想要使得总的解锁花费最小。
但是这次行动异常关键,Bessie不能满足于仅仅一个脱逃方案:她还需要后备方案。帮助她计算最小代价脱逃方案的数量;如果某一扇门在一个方案中被解锁了而在另一个方案中没有,那么这两个方案就被认为是不同的。
由于这个数字可能非常大,只需输出该数对10^9+7取模的余数。
Input
输入的第一行包含两个空格分隔的整数N和K(2≤N≤30000,2≤K≤6)。以下N行每行包含K−1个空格分隔的整数,为解锁一条水平边上的每扇门需要花费的代价。
以下K行每行包含N−1个空格分隔的整数,为解锁一条垂直边上的每扇门需要花费的代价。
所有的花费均在1到10^9之间。
对于20%的测试数据,保证N≤500,所有的代价均在1到5之间。
对于另外20%的测试数据,保证N≤5000。
Output
一个整数:最小花费的逃脱方案的数量,对10^9+7取模。
Sample Input
4 3
1 1
5 6
7 8
1 1
1 1 1
2 3 4
1 1 1
Sample Output
10
这个测试样例描述了一个4x3的方阵,
1 1
±----±----+
| | |
1 | |2 | 1
| 5 | 6 |
±----±----+
| | |
1 | |3 | 1
| 7 | 8 |
±----±----+
| | |
1 | |4 | 1
| | |
±----±----+
1 1
所有的最小代价脱逃方案都会使用代价为2的门,代价为3的门,以及某九个代价为1的门。由于有十种方式选择不被使用的代价为1的门,所以答案为10。
Data Constraint
思路
轮廓线DP
最小表示法压连通性
把每次转移的预处理出来
每次可以直接枚举2k横边情况和2k-1竖边情况转移
代码
#pragma GCC optimize (2)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e4+77,mo=1e9+7;
int n,k,R[N][10],D[N][10],o,tot;
ll f[2][140];
int g[2][140],a[10],b[10],h[10000000],w[10];
int q[140][10],sg[140][10],v[140],sep;
int yjy[140][10][2][2];
void add(int &x, int y)
{
x+=y; if(x>=mo)x-=mo;
}
void dfs(int x,int s)
{
if(x>k)
{
sep=h[s]=++tot;
v[tot]=s;
memcpy(q[tot],a,sizeof a);
for(int i=1; i<=k; i++)sg[tot][i]=(b[i]==1&&a[i]==i);
return;
}
int le=n;
for(int i=x-1; ~i; i--)
{
if(i>le)i=le;
if(a[i]==i)
{
if(i==0)b[x]=1,a[x]=x;
else
{
a[x]=i;
b[i]++;
}
dfs(x+1,s+a[x]*w[x]);
if(i!=0)b[i]--;
a[x]=0;
}
le=min(le,a[i]);
}
}
void init()
{
for(int y=1; y<=k; y++)
for(int s=1; s<=tot; s++)
{
for(int up=0; up<2; up++)
{
for(int le=0; le<(y==1?1:2); le++)
{
int ns=v[s];
if(up+le==0)
{
ns+=(y-q[s][y])*w[y];
if(q[s][y]==y)
{
int mi=k+1;
for(int i=y+1; i<=k; i++)if(q[s][i]==y)
{
mi=min(mi,i);
}
for(int i=y+1; i<=k; i++)if(q[s][i]==y)
{
ns+=(mi-q[s][i])*w[i];
}
}
}
else if(up+le==2)
{
int u=q[s][y],v=q[s][y-1];
int z=min(u,v);
for(int i=1; i<=k; i++)if(q[s][i]==u||q[s][i]==v)
{
ns+=(z-q[s][i])*w[i];
}
}
else if(up==1)
{
}
else if(le==1)
{
ns+=(q[s][y-1]-q[s][y])*w[y];
if(q[s][y]==y)
{
int mi=k+1;
for(int i=y+1; i<=k; i++)if(q[s][i]==y)
{
mi=min(mi,i);
}
for(int i=y+1; i<=k; i++)if(q[s][i]==y)
{
ns+=(mi-q[s][i])*w[i];
}
}
}
yjy[s][y][up][le]=h[ns];
}
}
}
}
int main()
{
freopen("escape.in","r",stdin); freopen("escape.out","w",stdout);
scanf("%d%d",&n,&k);
w[1]=1;
for(int i=2; i<=k; i++) w[i]=w[i-1]*10;
for(int i=1; i<=n; i++)
for(int j=1; j<k; j++) scanf("%d",&R[i][j]);
for(int j=1; j<=k; j++)
for(int i=1; i<n; i++) scanf("%d",&D[i][j]);
dfs(1,0);
init();
memset(f,127,sizeof f);
f[o][sep]=0,g[o][sep]=1;
for(int x=1; x<=n; x++)
{
for(int y=1; y<=k; y++)
{
o^=1;
memset(f[o],127,sizeof f[o]);
memset(g[o],0,sizeof g[o]);
for(int s=1; s<=tot; s++)if(g[o^1][s])
{
for(int up=0; up<(x==1?1:2); up++)
{
if(up==0&&x!=1&&sg[s][y]) continue;
for(int le=0; le<(y==1?1:2); le++)
{
int ns=yjy[s][y][up][le];
if(ns==0) continue;
ll nf=f[o^1][s]+le*R[x][y-1]+up*D[x-1][y];
if(f[o][ns]==nf)
{
add(g[o][ns],g[o^1][s]);
}
else if(f[o][ns]>nf)
{
f[o][ns]=nf;
g[o][ns]=g[o^1][s];
}
}
}
}
}
}
printf("%d",g[o][1]);
}