解法:半枚举、动态规划。
首先是观察数据规模发现很小,于是可以在上做文章(这是我一直以为是状压DP的原因。。。)——将行的方向枚举出来,这样变量就只有列的方向了。
以下都是在行的方向确定情况进行的讨论。
然后对于一个要求、,与,与关系确定,我们画图可以发现所有的情况都可以等效为一下4种(不包括或情况):

将其归纳一下,就是:
当行、行符合方向时,列中要有一个符合方向。
当只有行符合方向时,列要符合方向。
当只有行符合方向时,列要符合方向。
当行、行都不符合方向时,、要同时符合方向。
然后讨论x1=x2或y1=y2的情况:
当且时,无论方向都符合。
当且时,行要符合方向。
当且时,列要符合方向。
因为、行的方向建立在半枚举的基础上,所以当不满足方向时,就是无效状态。
排除无效状态后,设为1,为0,将列的方向看作长度为的数字串,那么上面列的满足条件就都可以等价于区间中要有一个为0或1。
设为0为0区间,为1为1区间。
问题就变成了求最小代价使所有0、1区间全部得到满足,于是可以用动态规划解决。
DP之前将所有的0、1区间讨论处理出来,分别按l为第一关键字,r为第二关键字排序,作为DP基础。
DP之前还有一个小小的剪枝,就是对于0、1性质相同的区间,如果存在包含关系,则去掉大区间,因为小区间满足大区间也肯定满足,这个优化的效果十分显著。
设表示把i列方向变成1的代价,对应的有。
动态规划的方程是表示前列状态确定,且满足了排序后的所有编号在的0区间与所有编号在的1区间的最小代价。
这样通过讨论第i列的状态,就可以转移到。其中若第i列方向为0,则为第一个左界>的0区间,为,转移时要加上;若第列方向为1,则为,第一个左界>的1区间,转移时要加上。
注意枚举还需要有左边界、代表第一个包含i的区间,而所有的右边界<的区间应该在枚举之前全部保证满足。因为没有左边界会从无效状态直接跳到、,漏掉很多区间限制。
每一个的与都可以预处理出来,这样递推取最小值就可以了。边界是,结果是当前行状态的生成代价
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=111, maxl = 201, inf = 100000000;
int n, m, K, SM, SN[maxn], r[3], rr[3][maxn], ll[3][maxn], costm[maxn][3], costn[maxn][3];
int sm[maxn], sn[maxn], vm[maxn], vn[maxn], f[maxn][maxl][maxl], pp[maxn][maxl][maxl];
char s[maxn], tm[3], tn[3];
bool g[maxn][maxl][maxl];
struct req { int x1, x2, y1, y2; } a[maxn];
struct inter { int l, r; } in[3][maxl];
void init()
{
scanf("%d%d", &m, &n);
scanf("%s", s);
for (int i=1; i<=m; ++i) sm[i] = (s[i-1]=='E');
scanf("%s", s);
for (int i=1; i<=n; ++i) sn[i] = (s[i-1]=='S');
for (int i=1; i<=m; ++i)
{
scanf("%d", vm+i);
costm[i][sm[i]] = 0, costm[i][1-sm[i]] = vm[i];
}
for (int i=1; i<=n; ++i)
{
scanf("%d", vn+i);
costn[i][sn[i]] = 0, costn[i][1-sn[i]] = vn[i];
}
scanf("%d", &K);
for (int i=1; i<=K; ++i)
scanf("%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
tm[0] = 'W', tm[1] = 'E', tn[0] = 'N', tn[1] = 'S';
}
inline bool cmp(const inter &a,const inter &b)
{ return a.l<b.l || (a.l==b.l && a.r<b.r); }
void maintain()
{
sort(in[0]+1,in[0]+r[0]+1,cmp);
sort(in[1]+1,in[1]+r[1]+1,cmp);
for (int q=0; q<2; ++q)
{
for (int i=1; i<=n; ++i)
{
int j = 1; ll[q][i] = 1;
for (; j<=r[q]; ++j)
if (in[q][j].l>i) break;
else if (in[q][j].r<i) ll[q][i] = j+1;
rr[q][i] = j;
}
}
}
void add(int p, int L, int R)
{
if (L>R) swap(L,R);
for (int i=1; i<=r[p]; ++i)
if (L<=in[p][i].l && in[p][i].r<=R) return;
in[p][++r[p]].l = L, in[p][r[p]].r = R;
}
bool prepare(int s)
{
memset(rr, 0 ,sizeof rr);
memset(ll, 0, sizeof ll);
r[0] = 0, r[1] = 0;
for (int i=1; i<=K; ++i)
{
int x1 = a[i].x1, y1 = a[i].y1, x2 = a[i].x2, y2 = a[i].y2;
int p = (x1<x2), q = (y1<y2);
if (x1!=x2 && y1!=y2)
{
if (((s >> (x1-1)) & 1)==q && ((s >> (x2-1)) & 1)==q) add(p,y1,y2);
else if (((s >> (x1-1)) & 1)==q) add(p,y2,y2);
else if (((s >> (x2-1)) & 1)==q) add(p,y1,y1);
else
{
int t = 0, fr=min(x1,x2)+1, en=max(x1,x2);
for (int j=fr; j<en; ++j)
if (((s >> (j-1)) & 1)==q) t = 1;
if (!t) return 0;
add(p,y1,y1); add(p,y2,y2);
}
}
else if (x1==x2 && y1!=y2 && ((s >> (x1-1)) & 1) != q) return 0;
else if (x1!=x2 && y1==y2) add(p,y1,y2);
}
maintain();
return 1;
}
int dp()
{
memset(f, 0x7, sizeof f);
f[1][1][1] = 0;
for (int i=1; i<=n; ++i)
for (int j=ll[0][i]; j<=rr[0][i]; ++j)
for (int k=ll[1][i]; k<=rr[1][i]; ++k)
if (f[i][j][k]<inf)
{
if (f[i+1][rr[0][i]][k]>f[i][j][k]+costn[i][0])
{
f[i+1][rr[0][i]][k] = f[i][j][k]+costn[i][0];
g[i+1][rr[0][i]][k] = 0;
pp[i+1][rr[0][i]][k] = j;
}
if (f[i+1][j][rr[1][i]]>f[i][j][k]+costn[i][1])
{
f[i+1][j][rr[1][i]] = f[i][j][k]+costn[i][1];
g[i+1][j][rr[1][i]] = 1;
pp[i+1][j][rr[1][i]] = k;
}
}
return f[n+1][r[0]+1][r[1]+1];
}
void get_SN(int i, int j, int k)
{
if (i==1) return;
SN[i-1] = g[i][j][k];
if (!g[i][j][k]) get_SN(i-1,pp[i][j][k],k);
else get_SN(i-1,j,pp[i][j][k]);
}
void work()
{
int tot = (1<<m), ans = inf;
for (int i=0; i<tot; ++i)
{
int t = 0;
for (int j=1; j<=m; ++j) t += costm[j][(i>>(j-1))&1];
if (prepare(i))
{
t += dp();
if (t<ans)
{
ans = t, SM = i;
get_SN(n+1,r[0]+1,r[1]+1);
}
}
}
if (ans<inf)
{
printf("possible\n");
printf("%d\n", ans);
while (m--) printf("%c", tm[SM & 1]), SM >>= 1;
printf("\n");
for (int i=1; i<=n; ++i)printf("%c", tn[SN[i]]);
printf("\n");
} else printf("impossible\n");
}
int main()
{
//freopen("manhattan.in", "r", stdin);
//freopen("manhattan.out", "w", stdout);
init();
work();
return 0;
}
虽然要枚举所有的行状态、列、、,理论极限时间复杂度是,但是由于dp之前的排除大区间的优化,实际上是远远达不到的,因为大部分的0、1区间长度都是1,所以不用担心超时的问题。