[SDOI2007]游戏
https://zybuluo.com/ysner/note/1184420
题面
题意简单,但不太好概括。
戳我
解析
不成熟想法
据题意可知,字符串字符的顺序无影响。
并且判断两个字符串能否接龙可以通过字符串哈希\(O(1)\)判断。
然后作为一个不会最长路算法的***,我自动进入了***流程。
- 把所有字符串按长度排序
- 按顺序一一判断长度相差为\(1\)的字符串是否可以形成接龙关系,并建边
- 注意到全图不一定联通且有环
- 全图建正边跑\(SPFA\)(以下边权均为负)
- 找出全图\(dis\)值最大的点,建反边继续跑\(SPFA\)
- 输出最长路长度,建正边回溯最长路输出方案
接着因为\(n\)这么统计会多\(1\),于是发生了代码\(3k\)、能过样例却爆\(0\)的惨案。。。
while(scanf("%s",a[++n].s+1)!=EOF) a[n].len=strlen(a[n].s+1);
改完后有\(40pts\)。
代码太长太丑,就别看了
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define ll unsigned long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=3e4+100;
int n,p[30],tot,cnt,h[N],in[N],dis[N],sta[N],top,tp;
bool vis[N];
struct Edge{int to,nxt;}e[N*100];
struct dat
{
int len;ll Hash;char s[111];
bool operator < (const dat &o) const {return len<o.len;}
}a[N];
struct node
{
int u,dis;
bool operator < (const node &o) const {return dis>o.dis;}
};
struct ysn{int u,v;}b[N*100];
priority_queue<node>Q;
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
il void wri(re int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) wri(x/10);
putchar(x%10+'0');
}
il void Pre()
{
memset(h,-1,sizeof(h));
fp(i,41,1000)
{
re int flag=1;
fp(j,2,sqrt(i))
if(i%j==0) {flag=0;break;}
if(flag) p[++tot]=i;
if(tot>26) break;
}
while(scanf("%s",a[++n].s+1)!=EOF) a[n].len=strlen(a[n].s+1);--n;
}
il void SPFA(re int st)
{
dis[st]=-1;vis[st]=1;Q.push((node){st,0});
while(!Q.empty())
{
re int u=Q.top().u;Q.pop();
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(dis[v]>dis[u]-1)
{
dis[v]=dis[u]-1;
if(!vis[v]) vis[v]=1,Q.push((node){v,dis[v]});
}
}
vis[u]=0;
}
}
il void dfs(re int u)
{
sta[++tp]=u;
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(dis[v]==dis[u]+1)
dfs(v);
}
}
int main()
{
//freopen("2.in","r",stdin);
Pre();
fp(i,1,n)
{
a[i].Hash=1;
fp(j,1,a[i].len)
a[i].Hash*=(a[i].s[j]*p[a[i].s[j]-96]);
}
sort(a+1,a+1+n);
/*fp(i,1,n)
{
fp(j,1,a[i].len) putchar(a[i].s[j]);
printf(" %d %lld %d\n",a[i].len,a[i].Hash,i);
}*/
re int l=1,r1,r2;
while(l<=n)
{
r1=r2=l;
while(a[r1].len==a[l].len&&r1<=n) ++r1;
r2=r1;r1--;
while(a[r2].len==a[l].len+1&&r2<=n) ++r2;--r2;
//printf("%d %d %d\n",l,r1,r2);
fp(i,l,r1)
fp(j,r1+1,r2)
fp(k,1,26)
if(a[i].Hash*(k+96)*p[k]==a[j].Hash) add(i,j),in[i]++,in[j]++,b[++top]=(ysn){i,j};//printf("%d %d\n",i,j);
l=r1+1;
}
fp(i,1,n) dis[i]=1e9,vis[i]=0;
fp(i,1,n) if(dis[i]==1e9&&in[i]) SPFA(i);
re int ans=0;
//fp(i,1,n) printf("%d ",-dis[i]);puts("");
fp(i,1,n) if(-dis[ans]<-dis[i]) ans=i;
memset(h,-1,sizeof(h));cnt=0;
fp(i,1,top) add(b[i].v,b[i].u);
fp(i,1,n) dis[i]=1e9,vis[i]=0;
SPFA(ans);re int pos;
ans=0;
fp(i,1,n) if(ans<-dis[i]) ans=-dis[i],pos=i;
memset(h,-1,sizeof(h));cnt=0;
fp(i,1,top) add(b[i].u,b[i].v);
dfs(pos);
printf("%d\n",ans);
fp(i,1,tp)
{
fp(j,1,a[sta[i]].len) putchar(a[sta[i]].s[j]);
puts("");
}
return 0;
}
建边判断
- 把所有字符串按长度排序
- 按顺序一一判断长度相差为\(1\)的字符串是否可以形成接龙关系,并建边
最坏复杂度可以达到\(O(n^2)\)
。。。
脑子短路了,直接在每个字符串枚举加\(26\)字符,看形成的字符串是否存在不就成了吗?
复杂度降为\(O(26n)\)
最长路算法
对于求最长路,我一开始的思路是建负边跑\(SPFA\)。这种思路固然正确,但不适用于图不联通(或有负环)的情况。
在图无环(由题目性质决定)时,可用拓扑排序来求最长路:
- 找出无入度的点入栈
- 修改\(dis\)距离值,删去其出度边
- 若有点因此入度减少为\(0\),入栈
此题要输出方案,用\(pr\)数组记一下前驱(随便哪次修改的前驱都可以)即可。
\(Hash\)策略的修改
我一开始的\(Hash\)策略是(\(s\)为当前枚举到的字符串,\(p\)为自己选的\(26\)个不同素数)
\]
发现这样很容易重复(因为字符串可以很长,乘的结果很大,取模后冲突概率大)。
观察发现字符串之间每个字符的出现次数出现不同的概率大得多。
于是策略改为(\(tong[i]\)为第\(i\)个小写字母在该字符串中的出现次数)
\]
判重用\(unordered\_map\)又准又快。
// luogu-judger-enable-o2
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<tr1/unordered_map>
#define ll unsigned long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=3e4+100;
int n,cnt,h[N],in[N],dis[N],ans,pos,pr[N],sta[N],tp;
std::tr1::unordered_map<ll,int>vis;
queue<int>Q;
struct Edge{int to,nxt;}e[N*500];
struct dat
{
int len,tong[27];ll Hash;char s[111];
}a[N];
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;++in[v];}
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void Pre()
{
memset(h,-1,sizeof(h));
while(scanf("%s",a[++n].s+1)!=EOF) a[n].len=strlen(a[n].s+1);--n;
}
il ll mHash(re int *tong)
{
re ll sum=1;
fp(i,1,26) sum*=(233+i+tong[i]);
return sum;
}
int main()
{
//freopen("1.in","r",stdin);
Pre();
fp(i,1,n)
{
a[i].Hash=1;
fp(j,1,a[i].len) ++a[i].tong[a[i].s[j]-96];
a[i].Hash=mHash(a[i].tong);
vis[a[i].Hash]=i;
//printf("%d %llu\n",i,a[i].Hash);
}
fp(i,1,n)
fp(j,1,26)
{
++a[i].tong[j];
re int now=vis[mHash(a[i].tong)];
if(now) add(i,now);//printf("!!!%d %d\n",i,now);
--a[i].tong[j];
}
fp(i,1,n) if(!in[i]) Q.push(i),dis[i]=1;
while(!Q.empty())
{
re int u=Q.front();Q.pop();
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
dis[v]=dis[u]+1;pr[v]=u;
if(!(--in[v])) Q.push(v);
}
}
fp(i,1,n) if(dis[i]>ans) ans=dis[pos=i];
while(pos) sta[++tp]=pos,pos=pr[pos];
printf("%d\n",ans);
fq(i,tp,1)
{
fp(j,1,a[sta[i]].len) putchar(a[sta[i]].s[j]);puts("");
}
return 0;
}
[SDOI2007]游戏的更多相关文章
- luogu P2462 [SDOI2007]游戏
LINK:SDOI2007游戏 题意:接龙前一个要比后面大1 且后一个单词出现的各自字母的次数>=前一个单词各自的字母的次数 考虑暴力dp sort之后dpY 显然会T. 考虑我们没必要枚举j ...
- 洛谷P2462 [SDOI2007]游戏(哈希+最长路)
题面 传送门 题解 我们把字符的出现次数哈希起来,然后把每个点向能在它之后的点连边.那么这显然是一个\(DAG\),直接求最长路就行了 //minamoto #include<bits/stdc ...
- 字符串Hash/树Hash学习笔记
哈希 Tags:字符串 作业部落 评论地址 一.概述 百度百科: 散列表(Hash table/哈希表),是根据关键码值(Key value)而直接进行访问的数据结构. 哈希表常用于比较两个字符串是否 ...
- 使用HTML5开发Kinect体感游戏
一.简介 我们要做的是怎样一款游戏? 在前不久成都TGC2016展会上,我们开发了一款<火影忍者手游>的体感游戏,主要模拟手游章节<九尾袭来 >,用户化身四代,与九尾进行对决, ...
- jQuery实践-网页版2048小游戏
▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...
- Unity游戏内版本更新
最近研究了一下游戏内apk包更新的方法. ios对于应用的管理比较严格,除非热更新脚本,不太可能做到端内大版本包的更新.然而安卓端则没有此限制.因此可以做到不跳到网页或应用商店,就覆盖更新apk包. ...
- 游戏服务器菜鸟之C#初探一游戏服务
本人80后程序猿一枚,原来搞过C++/Java/C#,因为工作原因最后选择一直从事C#开发,因为读书时候对游戏一直比较感兴趣,机缘巧合公司做一个手游的项目,我就开始游戏服务器的折腾之旅. 游戏的构架是 ...
- iOS审核这些坑,腾讯游戏也踩过
作者:Jamie,专项技术测试工程师,在iOS预审和ASO优化领域从事专项测试相关工作,为腾讯游戏近100个产品提供专项服务. WeTest 导读 在App上架苹果应用商店的过程中,相信大多数iOS开 ...
- 漫谈C#编程语言在游戏领域的应用
0x00 前言 随着微软越来越开放,C#也变得越来越吸引人们的眼球.而在游戏行业中,C#也开始慢慢地获得了关注.这不, 网易绝代双娇手游团队已经全面使用.Net Core支持前后端统一C#开发,跨平台 ...
随机推荐
- 支付宝小程序日期选择组件datePicker封装
github 地址 https://github.com/iocool/antminDatePicker 最近在做支付宝小程序(以下简称小程序)开发,发现小程序的日期选择组件很不好用,比如安卓和IOS ...
- Oracle、Db2、SqlServer、MySQL 数据库插入当前系统时间
做易买网项目,由于对数据库插入系统时间不了解,常常遇到的问题: 1.java.sql.SQLException: ORA-01861: 文字与格式字符串不匹配.原因:由于获取系统时间类型不对,应为sy ...
- vue中使用Swiper
第一步:安装swiper在项目目录下打开命令窗口输入命令:npm install swiper 第二步:引入js文件 第三步:引入css文件在main.js文件中引入css文件
- 微信小程序animation
wxml <view class="background" animation="{{rotateData}}"> </view>< ...
- Linux培训时长多久可以学会?马哥教育9年经验之谈
在Linux的热潮下,很多人萌发了学习Linux的想法.比起自学,培训是一个能够快速.系统的掌握知识的方式,也受到了不少人的青睐. 很多人都想知道通过培训学习Linux需要多长时间,今天咱们就来盘点一 ...
- swift-延时加载函数
//延时加载函数 func delayLoad(){ let time: NSTimeInterval = 2.0 let delay = dispatch_time(DISPATCH_TIME_NO ...
- Linux - redis发布|订阅
目录 Linux - redis发布|订阅 发布|订阅 基本命令 发布和订阅实例 正则方式订阅一个或者多个符合模式的频道 Linux - redis发布|订阅 发布: publish 订阅: subs ...
- JavaScript单元测试工具-Jest
标注: 首先这并不是一篇完整的关于Jest的教程,只是个人在接触jest学习的一点随手笔记,大部分内容都是对官方文档的一些翻译. ----------------------------------- ...
- 【Codeforces 1114D】Flood Fill
[链接] 我是链接,点我呀:) [题意] 你选择一个point作为start_position 然后每次你可以将包含该start_position的所有联通块变成任意颜色 问你最少要多少次变换才能将所 ...
- Maven学习总结(2)——Maven项目构建过程练习
Maven学习总结(二)--Maven项目构建过程练习 上一篇只是简单介绍了一下maven入门的一些相关知识,这一篇主要是体验一下Maven高度自动化构建项目的过程 一.创建Maven项目 1.1.建 ...