电路维修
【题目描述】:
电路维修
【思路】:
开先看起来挺迷的,觉得没法做,搜索也写不出来。。。
再仔细分析题目,发现对于任意一根电路,它只有两种状态:
- 连着右上和坐下
- 连着左下和右上
要求最小的操作数。
我们可以发现,每条电线可以花费1的代价从一种状态改变为另外一种状态,那么我们可以对于已经连着的两点连一条权为0的边,没连着的连一条花费为1的边,求出从左上角到右下角的最短路即可。
注:卡\(SPFA\),建议用\(SLF\) \(or\) \(LLL\) \(or\) 堆优化的\(DJ\),此处用的\(SLF\)优化。
#include<cstdio>
#include<queue>
#include<cstring>
#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;
}