【NOI2004】 曼哈顿
题目描述
曼哈顿 【问题描述】 P城是M国的著名旅游城市。在市长G先生的治理下,人民安居乐业,城市欣欣向荣。然而,G市长并没有被自己的政绩冲昏头脑,他清醒地意识到城市的治理还存在着一些问题,其中之一就是交通问题。 P城有m条横向街道和n条纵向街道,横向街道横贯东西,纵向街道纵穿南北,构成了P城整齐的交通网络(如图1所示)。 图1 由于街道狭窄,每条街道都只允许单向行驶,单向行驶的方向是事先设定好了的。一条横向街道的行驶方向只能是向东或者向西,一条纵向街道的行驶方向只能是向南或者向北,逆向行驶是绝对禁止的。 这项限制给交通带来了巨大的不便。如图1,很多游人希望从宾馆前往购物中心,但限于街道的行驶方向,他们不得不绕一个大圈才能够到达。 这个问题一直困扰着G市长,每天他都会收到不少游人的来信,抱怨P城不合理的交通设计。但由于街道数目过多,他和他的部下始终不能解决这个问题…… 令人高兴的是这个问题不久就可能得以解决。因为最近他们以重金聘请了著名的交通规划大师B先生,请他对P城的交通进行有效合理的改造。 B先生知道不能通过拓宽街道的方法解决问题,因为这样势必影响到街道两旁的旅游景点,这是大家都不希望看到的。于是他准备重新设计街道的行驶方向(整条街道的行驶方向),使之尽可能满足大家的要求。 B先生先把P城的街道编号,横向街道由北向南编号为1,2,……,m,纵向街道由西向东编号为1,2,……,n。这样任何一个十字路口的位置都可以用一对正整数来表示,第一个数是该路口所在的横向街道的编号,第二个数是它所在的纵向街道的编号,这对整数被称为该十字路口的坐标。比如图1中宾馆所在的十字路口的坐标是(2,3)。 经过长期调查,他整理出了游人们提得相对集中的一些要求。每条要求都可以写成如下的形式:从一个十字路口到另一个十字路口的最短路径的长度必须等于它们之间的曼哈顿距离。所谓曼哈顿距离是指两个十字路口在东西方向上的距离加上在南北方向上的距离,坐标分别为(x1,y1)和(x2,y2)的两个十字路口之间的曼哈顿距离为|x1-x2|+|y1-y2|。 好了,B先生已经知道了P城目前所有街道的行驶方向和游人们提得相对集中的要求,他能不能重新设计街道的行驶方向,使之满足所有要求呢? 另外,改变每条街道的行驶方向都有一定的工作量,工作量的大小因道路而异。B先生不仅想找到一个可行的改造计划,而且还希望这个计划的总工作量尽可能小。你能帮帮他吗? 【输入文件】 输入文件的第一行有两个正整数m和n,分别表示横向街道和纵向街道的数目。 第二行是一个长度为m的字符串,由北向南列出了m条横向街道改造前的行驶方向。E表示向东,W表示向西。 第三行是一个长度为n的字符串,由西向东列出了n条纵向街道改造前的行驶方向。S表示向南,N表示向北。 第四行有m个非负整数,由北向南列出了改变每条横向街道的行驶方向的工作量。 第五行有n个非负整数,由西向东列出了改变每条纵向街道的行驶方向的工作量。 第六行是一个正整数k,表示游人们提得相对集中的要求的数目。 接下来的k行,每行有四个正整数x1,y1,x2,y2,表示一条要求。这条要求的内容是希望从坐标为(x1,y1)的十字路口到坐标为(x2,y2)的十字路口的最短路径的长度等于这两个路口之间的曼哈顿距离。 【输出文件】 第一行是一个字符串,“possible”或者“impossible”(引号不输出)。输出“possible”表示可以通过改变街道的行驶方向满足输入数据中的所有要求,输出“impossible”表示无论怎么设计都不可能满足输入数据中的所有要求。 如果在第一行输出的是“possible”的话,在第二行输出一个整数,表示最小的总工作量,在第三行输出一个长度为m的字符串,由北向南列出改造后的m条横向街道的行驶方向,E表示向东,W表示向西,在第四行输出一个长度为n的字符串,由西向东列出改造后的n条纵向街道的行驶方向, S表示向南,N表示向北。 【约定】 改变一条街道的行驶方向的工作量不超过10000 【样例输入】 2 3 WE NNS 3 9 1 4 2 2 1 3 2 1 2 3 2 2 【样例输出】 possible 9 WW NNS 【评分方法】 如果你的输出文件的第一行是“impossible”, 如果确实无解,则该测试点满分。 如果实际有解,则该测试点0分。 如果你的输出文件的第一行是“possible”, 如果你的程序输出的方案不可行,则该测试点0分。 如果你的程序输出的总工作量与实际总工作量不一致,则该测试点0分。 如果你的程序输出的方案可行,但总工作量不是最小的,则该测试点4分。 如果你的程序输出的方案可行,且总工作量最小,则该测试点满分。
半枚举+动态规划
1 /* 2 *Problem: NOI2004 曼哈顿 3 *Author : Chen Yang 4 *Time : 2012.6.8 7:10 pm 5 *State : AC 6 *Memo : 半枚举、动态规划 7 */ 8 #include <cstdio> 9 #include <cstdlib> 10 #include <cstring> 11 #include <string> 12 #include <algorithm> 13 using namespace std; 14 const int maxn=111, maxl = 201, inf = 100000000; 15 int n, m, K, SM, SN[maxn], r[3], rr[3][maxn], ll[3][maxn], costm[maxn][3], costn[maxn][3]; 16 int sm[maxn], sn[maxn], vm[maxn], vn[maxn], f[maxn][maxl][maxl], pp[maxn][maxl][maxl]; 17 char s[maxn], tm[3], tn[3]; 18 bool g[maxn][maxl][maxl]; 19 struct req { int x1, x2, y1, y2; } a[maxn]; 20 struct inter { int l, r; } in[3][maxl]; 21 22 void init() 23 { 24 scanf("%d%d", &m, &n); 25 scanf("%s", s); 26 for (int i=1; i<=m; ++i) sm[i] = (s[i-1]=='E'); 27 scanf("%s", s); 28 for (int i=1; i<=n; ++i) sn[i] = (s[i-1]=='S'); 29 for (int i=1; i<=m; ++i) 30 { 31 scanf("%d", vm+i); 32 costm[i][sm[i]] = 0, costm[i][1-sm[i]] = vm[i]; 33 } 34 for (int i=1; i<=n; ++i) 35 { 36 scanf("%d", vn+i); 37 costn[i][sn[i]] = 0, costn[i][1-sn[i]] = vn[i]; 38 } 39 scanf("%d", &K); 40 for (int i=1; i<=K; ++i) 41 scanf("%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2); 42 tm[0] = 'W', tm[1] = 'E', tn[0] = 'N', tn[1] = 'S'; 43 } 44 45 inline bool cmp(const inter &a,const inter &b) 46 { return a.l<b.l || (a.l==b.l && a.r<b.r); } 47 48 void maintain() 49 { 50 sort(in[0]+1,in[0]+r[0]+1,cmp); 51 sort(in[1]+1,in[1]+r[1]+1,cmp); 52 for (int q=0; q<2; ++q) 53 { 54 for (int i=1; i<=n; ++i) 55 { 56 int j = 1; ll[q][i] = 1; 57 for (; j<=r[q]; ++j) 58 if (in[q][j].l>i) break; 59 else if (in[q][j].r<i) ll[q][i] = j+1; 60 rr[q][i] = j; 61 } 62 } 63 } 64 65 void add(int p, int L, int R) 66 { 67 if (L>R) swap(L,R); 68 for (int i=1; i<=r[p]; ++i) 69 if (L<=in[p][i].l && in[p][i].r<=R) return; 70 in[p][++r[p]].l = L, in[p][r[p]].r = R; 71 } 72 73 bool prepare(int s) 74 { 75 memset(rr, 0 ,sizeof rr); 76 memset(ll, 0, sizeof ll); 77 r[0] = 0, r[1] = 0; 78 for (int i=1; i<=K; ++i) 79 { 80 int x1 = a[i].x1, y1 = a[i].y1, x2 = a[i].x2, y2 = a[i].y2; 81 int p = (x1<x2), q = (y1<y2); 82 if (x1!=x2 && y1!=y2) 83 { 84 if (((s >> (x1-1)) & 1)==q && ((s >> (x2-1)) & 1)==q) add(p,y1,y2); 85 else if (((s >> (x1-1)) & 1)==q) add(p,y2,y2); 86 else if (((s >> (x2-1)) & 1)==q) add(p,y1,y1); 87 else 88 { 89 int t = 0, fr=min(x1,x2)+1, en=max(x1,x2); 90 for (int j=fr; j<en; ++j) 91 if (((s >> (j-1)) & 1)==q) t = 1; 92 if (!t) return 0; 93 add(p,y1,y1); add(p,y2,y2); 94 } 95 } 96 else if (x1==x2 && y1!=y2 && ((s >> (x1-1)) & 1) != q) return 0; 97 else if (x1!=x2 && y1==y2) add(p,y1,y2); 98 } 99 maintain(); 100 return 1; 101 } 102 103 int dp() 104 { 105 memset(f, 0x7, sizeof f); 106 f[1][1][1] = 0; 107 for (int i=1; i<=n; ++i) 108 for (int j=ll[0][i]; j<=rr[0][i]; ++j) 109 for (int k=ll[1][i]; k<=rr[1][i]; ++k) 110 if (f[i][j][k]<inf) 111 { 112 if (f[i+1][rr[0][i]][k]>f[i][j][k]+costn[i][0]) 113 { 114 f[i+1][rr[0][i]][k] = f[i][j][k]+costn[i][0]; 115 g[i+1][rr[0][i]][k] = 0; 116 pp[i+1][rr[0][i]][k] = j; 117 } 118 if (f[i+1][j][rr[1][i]]>f[i][j][k]+costn[i][1]) 119 { 120 f[i+1][j][rr[1][i]] = f[i][j][k]+costn[i][1]; 121 g[i+1][j][rr[1][i]] = 1; 122 pp[i+1][j][rr[1][i]] = k; 123 } 124 } 125 return f[n+1][r[0]+1][r[1]+1]; 126 } 127 128 void get_SN(int i, int j, int k) 129 { 130 if (i==1) return; 131 SN[i-1] = g[i][j][k]; 132 if (!g[i][j][k]) get_SN(i-1,pp[i][j][k],k); 133 else get_SN(i-1,j,pp[i][j][k]); 134 } 135 136 void work() 137 { 138 int tot = (1<<m), ans = inf; 139 for (int i=0; i<tot; ++i) 140 { 141 int t = 0; 142 for (int j=1; j<=m; ++j) t += costm[j][(i>>(j-1))&1]; 143 if (prepare(i)) 144 { 145 t += dp(); 146 if (t<ans) 147 { 148 ans = t, SM = i; 149 get_SN(n+1,r[0]+1,r[1]+1); 150 } 151 } 152 } 153 if (ans<inf) 154 { 155 printf("possible\n"); 156 printf("%d\n", ans); 157 while (m--) printf("%c", tm[SM & 1]), SM >>= 1; 158 printf("\n"); 159 for (int i=1; i<=n; ++i)printf("%c", tn[SN[i]]); 160 printf("\n"); 161 } else printf("impossible\n"); 162 } 163 164 int main() 165 { 166 freopen("manhattan.in", "r", stdin); 167 freopen("manhattan.out", "w", stdout); 168 init(); 169 work(); 170 return 0; 171 }