P3134 [USACO16JAN]Lights Out G 题解

P3134 [USACO16JAN]Lights Out G题解

题目大意

真不好概括,可以去看看讨论 (逃

分析

首先,我们可以发现,在黑暗中,要使 \(Bessie\) 意识到自己在哪个顶点,当且仅当她走过的边的结构在地图中是独一无二且最短的。

要实现这个目的,我们需要一种数据结构来维护地图的结构以及 \(Bessie\) 走过的路径,并且这种数据结构要满足如下几个特点:

  1. 可以查找路径在地图中的出现次数

  2. 支持在末尾新添状态

  3. 复杂度小

  4. 每种地图结构只有一种表达方式,反之亦然

这里给出一种很好实现的方法:字符串哈希

接下来说明如何实现。

实现

对于一个地图结构,我们可以模拟 \(Bessie\) 的路线:从起点出发,走一段长度到另一个顶点,然后左转或右转,直到到达出口

那我们也可以这样记录地图结构:

  1. 左转:在字符串中加入一个字符 \(L\)

  2. 右转:在字符串中加入一个字符 \(R\)

  3. 直走:在字符串中加入当前边的长度

可以得到如下函数:

inline void add(register string &mode,const int x){
	if(a[x].lr) mode+='R';else mode+='L';//左右转
	register string s;register int tmp=a[x].len;//路径长度
	while(tmp) s+=char((tmp%10)|48),tmp/=10;
	reverse(s.begin(),s.end());mode+=s;//加进字符串中
}

而要实现查找路径在地图中的出现次数,可以用到 \(STL\) 中的 \(string\) 函数 \(find()\)

循环使用 \(find()\) 即可:

inline int count(const string &a,const string &b){
	register size_t pos=0;register int ans=0;
	while((pos=a.find(b,pos))!=string::npos) ++ans,++pos;
	return ans;
}

\(But!!\) 仔细思考一下即可发现几个问题:

  1. 若路径串为 \(R10\),地图串中有一个子串为 \(R1065\),那么此时这个 \(count()\) 函数仍然会匹配到。

    所以我们对 \(add()\) 函数进行一些修改:每次加入完一条边后再加一个空格

    inline void add(register string &mode,const int x){
        if(a[x].lr) mode+='R';else mode+='L';//左右转
        register string s;register int tmp=a[x].len;//路径长度
        while(tmp) s+=char((tmp%10)|48),tmp/=10;
        reverse(s.begin(),s.end());mode+=s;mode+=' ';//新加一个空格(或其他字符也行)
    }
    
  2. 重新审题,可以发现 \(Bessie\) 每到一个顶点,她是可以判断下一条边的转向方向的(左 \(or\) 右)。

    所以我们对 \(count()\) 函数进行一些修改:每次匹配路径时,临时在路径串末尾加入下一次转向的符号

    inline int count(const string &a,register string b,const char c){
        b+=c;//临时加入下一次转向的方向符号
        register size_t pos=0;register int cnt=0;
        while((pos=a.find(b,pos))!=string::npos) ++cnt,++pos;
        return cnt;
    }
    

有了 \(add()\) 和 \(count()\) 函数,我们即可写出 \(solve()\) 函数统计答案

inline void solve(const int now){
	register string path;
	register int sum=0;
	for(register int i=now;i<=n;++i){
		if(count(mode,path,a[i].lr?'R':'L')) return void(ans=max(ans,abs(dis[now]-(sum+dis[i]))));
		add(path,i),sum+=a[i].len;
	}
}

for(register int i=2;i<=n;i++) solve(i);

接下来就是最恶心的数据读入

代码中注释很详细,可以自行理解

/*a[i].lr: 0->左转,1->右转*/
/*a[i].dir: 1->东,2->南,3->西,4->北*/
n=read();
for(register int i=1;i<=n;++i) mp[i].first=read(),mp[i].second=read();
mp[n+1]=mp[1];
for(register int i=2;i<=n+1;++i){
    if(mp[i].first==mp[i-1].first){//竖着走
        tot+=(a[i-1].len=abs(mp[i].second-mp[i-1].second));
        if(mp[i].second>mp[i-1].second){//向上走
            a[i-1].dir=4;if(!a[i-2].dir) continue;
            if(a[i-2].dir==1) a[i-1].lr=0;//前一个是向东走,那么就是左转
            else a[i-1].lr=1;//前一个是向西走,那么就是右转
        }
        else{//向下走
            a[i-1].dir=2;if(!a[i-2].dir) continue;
            if(a[i-2].dir==1) a[i-1].lr=1;//前一个是向东走,那么就是右转
            else a[i-1].lr=0;//前一个是向西走,那么就是左转
        }
    }
    else{//横着走
        tot+=(a[i-1].len=abs(mp[i].first-mp[i-1].first));
        if(mp[i].first>mp[i-1].first){//向右走
            a[i-1].dir=1;if(!a[i-2].dir) continue;
            if(a[i-2].dir==2) a[i-1].lr=0;//前一个是向南走,那么就是左转
            else a[i-1].lr=1;//前一个是向北走,那么就是右转
        }
        else{//向左走
            a[i-1].dir=3;if(!a[i-2].dir) continue;
            if(a[i-2].dir==2) a[i-1].lr=1;//前一个是向南走,那么就是右转
            else a[i-1].lr=0;//前一个是向北走,那么就是左转
        }
    }
}

完整代码

#include<bits/stdc++.h>
#define N 205
#define inf 0x3f3f3f3f
#define endl '\n'
#define debug cerr<<__LINE__<<endl
using namespace std;
int n,tot,ans=-inf;
int dis[N];
int shun[N],ni[N];
pair<int,int>mp[N];
struct node{int len,dir;bool lr;}a[N];
string mode;
inline char gc(){
	static const int L=1<<22|1;static char c[L],*a,*b;
	return (a==b)&&(b=(a=c)+fread(c,1,L,stdin),a==b)?-1:*a++;
}
inline int read(){
	register int f=1,k=0;
	register char c=gc();
	while(c!='-'&&(c<'0'||c>'9')) c=gc();
	if(c=='-') f=-1,c=gc();
	while(c>='0'&&c<='9') k=(k<<3)+(k<<1)+(c^48),c=gc();
	return f*k;
}
inline void write(register int x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar((x%10)|48);
}
inline void add(register string &mode,const int x){
	if(x==1) mode+='T';else if(a[x].lr) mode+='R';else mode+='L';
	register string s;register int tmp=a[x].len;
	while(tmp) s+=char((tmp%10)|48),tmp/=10;
	reverse(s.begin(),s.end());mode+=s;mode+=' ';
}
inline void init(){
	for(register int i=1;i<=n;i++) add(mode,i);
	for(register int sum=0,i=1;i<=n;i++) dis[i]=min(tot-sum,sum),sum+=a[i].len;
}
inline int count(const string &a,register string b,const char c){
	b+=c;
	register size_t pos=0;register int cnt=0;
	while((pos=a.find(b,pos))!=string::npos) ++cnt,++pos;
	return cnt;
}
inline void solve(const int now){
	register string path;
	register int sum=0;
	for(register int i=now;i<=n;i++){
		if(count(mode,path,a[i].lr?'R':'L')==1) return void(ans=max(ans,abs(dis[now]-(sum+dis[i]))));
		add(path,i),sum+=a[i].len;
	}
}
main(void){
	n=read();
	for(register int i=1;i<=n;i++) mp[i].first=read(),mp[i].second=read();
	mp[n+1]=mp[1];
	for(register int i=2;i<=n+1;i++){
		if(mp[i].first==mp[i-1].first){
			tot+=(a[i-1].len=abs(mp[i].second-mp[i-1].second));
			if(mp[i].second>mp[i-1].second){
				a[i-1].dir=4;if(!a[i-2].dir) continue;
				if(a[i-2].dir==1) a[i-1].lr=0;
				else a[i-1].lr=1;
			}
			else{
				a[i-1].dir=2;if(!a[i-2].dir) continue;
				if(a[i-2].dir==1) a[i-1].lr=1;
				else a[i-1].lr=0;
			}
		}
		else{
			tot+=(a[i-1].len=abs(mp[i].first-mp[i-1].first));
			if(mp[i].first>mp[i-1].first){
				a[i-1].dir=1;if(!a[i-2].dir) continue;
				if(a[i-2].dir==2) a[i-1].lr=0;
				else a[i-1].lr=1;
			}
			else{
				a[i-1].dir=3;if(!a[i-2].dir) continue;
				if(a[i-2].dir==2) a[i-1].lr=1;
				else a[i-1].lr=0;
			}
		}
	}
	init();for(register int i=2;i<=n;i++) solve(i);
	write(ans),putchar('\n');
	return 0;
}

码风较奇葩(压行强迫症+卡常小能手

欢迎觉得做法很麻烦或有错误的巨佬来喷

上一篇:【飞天存储服务月报】2018年8月刊


下一篇:Spring Cloud-鸿鹄Cloud分布式微服务云系统—System系统管理