1448:【例题1】电路维修
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 991 通过数: 304
【题目描述】
译自 BalticOI 2011 Day1 T3「Switch the Lamp On」
有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会。
有 N×M 个这样的元件,你想将其排列成 N 行 M 列放在电路板上。电路板的左上角连接电源,右下角连接灯泡。
试求:至少要旋转多少个正方形元件才能让电源与灯泡连通,若无解则输出 NO SOLUTION。
Casper is designing an electronic circuit on a N×M rectangular grid plate. There are N×M square tiles that are aligned to the grid on the plate. Two (out of four) opposite corners of each tile are connected by a wire.
A power source is connected to the top left corner of the plate. A lamp is connected to the bottom right corner of the plate. The lamp is on only if there is a path of wires connecting power source to lamp. In order to switch the lamp on, any number of tiles can be turned by
90° (in both directions).
In the picture above the lamp is off. If any one of the tiles in the second column from the right is turned by 90° , power source and lamp get connected, and the lamp is on.
Write a program to find out the minimal number of tiles that have to be turned by 90° to switch the lamp on.
【输入】
有多组测试数据。
第一行为测试数据组数,以下每组测试数据描述为:
第一行有两个整数 N 和 M。
在接下来的 N 行中,每行有 M 个字符。每个字符均为 “” 或 “/”,表示正方形元件上导线的连接方向。
The first line of input contains two integer numbers N and M, the dimensions of the plate. In each of the following N lines there are M symbols – either \ or / – which indicate the direction of the wire connecting the opposite vertices of the corresponding tile.
【输出】
每组测试数据输出描述:
输出共一行,若有解则输出一个整数,表示至少要旋转多少个正方形元件才能让电源与灯泡连通;若无解则输出 NO SOLUTION。
There must be exactly one line of output. If it is possible to switch the lamp on, this line must contain only one integer number: the minimal number of tiles that have to be turned to switch on the lamp. If it is not possible, output the string: NO SOLUTION
【输入样例】
1
3 5
\/\
\///
/\\
【输出样例】
1
【提示】
对于 40% 的数据,1≤N≤4,1≤M≤5。
对于所有数据,1≤N,M≤500。
思路:任意一根电路,它只有两种状态:
连着右上和左下
连着左下和右上
每条电线可以花费1的代价从一种状态改变为另外一种状态,那么我们可以对于已经连着的两点连一条权为0的边,没连着的连一条花费为1的边,求出从左上角到右下角的最短路即可。
spfa的SLF优化就是small label first 优化,当加入一个新点v的时候如果此时的dis[v]比队首dis[q.front()]还要小的话,就把v点加入到队首,否则把他加入到队尾,因为先扩展最小的点可以尽量使程序尽早的结束,一种方法可以用模拟队列,head,tail,但是由于不知道q的数组开多大,所有用双端队列dequeq更好些.
#include
#include
#include
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
const int MAXN = 251005;
struct edge{
int u,v,w,nxt;
}e[MAXN<<2];int head[MAXN];int cnt = 0;int ed;int dis[MAXN];bool vis[MAXN];char c[505];
inline void add(int u,int v,int w){
e[++cnt].u = u;e[cnt].v = v;e[cnt].w = w;e[cnt].nxt = head[u];head[u] = cnt;
}
deque<int>q;
inline void spfa(){
q.push_back(1);
memset(vis,0,sizeof vis);
memset(dis,inf,sizeof dis);
dis[1] = 0;
while(!q.empty()){
int u = q.front();q.pop_front();vis[u] = 0;
for(int i=head[u];i;i=e[i].nxt){
int v = e[i].v;
if(dis[v] > dis[u] + e[i].w){
dis[v] = dis[u] +e[i].w;
if(!vis[v]){
vis[v] = 1;
if(e[i].w == 0) q.push_front(v);
else q.push_back(v);
}
}
}
}
printf("%d\n",dis[ed]);
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
memset(head,0,sizeof head);cnt = 0;
for(int i=1;i<=n;++i){
scanf("%s",c+1);
for(int j=1;j<=m;++j){
if(c[j] == '/'){
add(j+1+(i-1)*(m+1) , j+i*(m+1) , 0);
add(j+i*(m+1) , j+1+(i-1)*(m+1) , 0);
add(j+(i-1)*(m+1) , j+1+i*(m+1) , 1);
add(j+1+i*(m+1) , j+(i-1)*(m+1) , 1);
}
else{
add(j+1+(i-1)*(m+1) , j+i*(m+1) , 1);
add(j+i*(m+1) , j+1+(i-1)*(m+1) , 1);
add(j+(i-1)*(m+1) , j+1+i*(m+1) , 0);
add(j+1+i*(m+1) , j+(i-1)*(m+1) , 0);
}
}
}
if((n+m)%2) {puts("NO SOLUTION");continue;}
ed = (n + 1) * (m + 1);
spfa();
}
return 0;
}