T1:秘密通道
本
题
看
上
去
像
b
f
s
,
但
发
现
假
了
,
因
为
不
知
道
通
道
什
么
时
候
走
本题看上去像bfs,但发现假了,因为不知道通道什么时候走
本题看上去像bfs,但发现假了,因为不知道通道什么时候走
因
为
n
<
=
500
,
我
们
发
现
可
存
数
组
远
大
于
N
∗
N
,
所
以
我
们
可
以
考
虑
对
于
二
元
组
(
x
,
y
)
之
间
建
边
因为n<=500,我们发现可存数组远大于N*N,所以我们可以考虑对于二元组(x,y)之间建边
因为n<=500,我们发现可存数组远大于N∗N,所以我们可以考虑对于二元组(x,y)之间建边
对
于
二
元
组
建
边
,
可
以
考
虑
m
a
p
+
p
a
i
r
,
也
可
以
之
间
将
(
x
,
y
)
h
a
s
h
一
下
,
用
(
m
∗
x
+
y
)
表
示
h
e
a
d
数
组
,
这
样
就
可
以
用
链
式
前
向
星
存
边
了
,
我
们
发
现
还
有
多
出
来
的
边
,
即
打
开
通
道
节
省
的
时
间
的
边
,
所
以
即
(
x
,
y
)
连
出
去
有
至
多
8
条
边
,
所
以
总
边
数
至
多
N
∗
N
∗
8
,
所
以
用
d
i
j
s
t
r
a
O
(
M
l
o
g
N
)
能
过
对于二元组建边,可以考虑map+pair,也可以之间将(x,y)hash一下,用(m*x+y)表示head数组,这样就可以用链式前向星存边了,我们发现还有多出来的边,即打开通道节省的时间的边,所以即(x,y)连出去有至多8条边,所以总边数至多N*N*8,所以用dijstra \color{blue}{O(MlogN)}能过
对于二元组建边,可以考虑map+pair,也可以之间将(x,y)hash一下,用(m∗x+y)表示head数组,这样就可以用链式前向星存边了,我们发现还有多出来的边,即打开通道节省的时间的边,所以即(x,y)连出去有至多8条边,所以总边数至多N∗N∗8,所以用dijstraO(MlogN)能过
注
意
点
:
\color{green}{注意点}:
注意点:
- 本题不是正方形,所以ok数组要(1<=y&&y<=n)
- hash一定要mx+y,因为m的值有可能大于y,所以nx+y会重,(调了很久)
- 本题的边是单向边,因为从一个能到另一个不代表从另一个能到这个
#include<bits/stdc++.h>
using namespace std;
const int N=505;
struct edge{
int link,vx,vy,lon;
}q[N*N*20];
int head[N*N+N],cnt=0;
int n,m;
char s[N][N];
void put(int x,int y,int xx,int yy,int z){
q[++cnt].vy=yy;
q[cnt].vx=xx;
q[cnt].lon=z;
q[cnt].link=head[m*x+y];//
head[m*x+y]=cnt;
}//
bool ok(int x,int y){
return (1<=x&&x<=n&&1<=y&&y<=m);//
}
int nf[4]={2,3,0,1};
int sx,sy,tx,ty,vis[N][N],up[N][N],dn[N][N],rht[N][N],lft[N][N],lon[N][N],nx[N][N][4],ny[N][N][4];
struct node{
int val,idx,idy;
bool operator <(const node&x)const{
return x.val<val;
}
};
int diss[N][N];
priority_queue<node> myline;
bool ex[N][N];
void dijstra(){
while(!myline.empty()) myline.pop();
myline.push((node){0,sx,sy});
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
diss[i][j]=N*N*16;
ex[i][j]=0;
}
diss[sx][sy]=0;
while(!myline.empty()){
node uu=myline.top();myline.pop();
int ux=uu.idx,uy=uu.idy;
if(ex[ux][uy]) continue;
ex[ux][uy]=1;
for(int i=head[ux*m+uy];i;i=q[i].link){
int vx=q[i].vx,vy=q[i].vy;
if(ok(vx,vy)){
if(diss[vx][vy]>diss[ux][uy]+q[i].lon){
diss[vx][vy]=diss[ux][uy]+q[i].lon;
myline.push((node){diss[vx][vy],vx,vy});
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",(s[i]+1));
for(int j=1;j<=m;j++){
if(s[i][j]=='#') vis[i][j]=1;
if(s[i][j]=='.') vis[i][j]=0;
if(s[i][j]=='C') vis[i][j]=2,sx=i,sy=j;
if(s[i][j]=='F') vis[i][j]=3,tx=i,ty=j;
lon[i][j]=N*N*16;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]==1) up[i][j]=i,lft[i][j]=j;
if(!up[i][j]) up[i][j]=up[i-1][j];
if(!lft[i][j]) lft[i][j]=lft[i][j-1];
if(vis[i][j]==0||vis[i][j]==3) {
if(lft[i][j]){
nx[i][j][2]=i,ny[i][j][2]=lft[i][j]+1;
lon[i][j]=min(lon[i][j],j-lft[i][j]);
}
if(up[i][j]){
nx[i][j][1]=up[i][j]+1,ny[i][j][1]=j;
lon[i][j]=min(lon[i][j],i-up[i][j]);
}
}
if(vis[i][j]==2){//
if(lft[i][j]){
nx[i][j][2]=i,ny[i][j][2]=lft[i][j]+1;
lon[i][j]=min(lon[i][j],j-lft[i][j]);
}
if(up[i][j]){
nx[i][j][1]=up[i][j]+1,ny[i][j][1]=j;
lon[i][j]=min(lon[i][j],i-up[i][j]);
}
}
}
}
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
if(vis[i][j]==1) dn[i][j]=i,rht[i][j]=j;
if(!dn[i][j]) dn[i][j]=dn[i+1][j];
if(!rht[i][j]) rht[i][j]=rht[i][j+1];
if(vis[i][j]==0||vis[i][j]==3) {
if(rht[i][j]){
nx[i][j][0]=i,ny[i][j][0]=rht[i][j]-1;
lon[i][j]=min(lon[i][j],rht[i][j]-j);
}
if(dn[i][j]){
nx[i][j][3]=dn[i][j]-1,ny[i][j][3]=j;
lon[i][j]=min(lon[i][j],dn[i][j]-i);
}
}
if(vis[i][j]==2){
if(rht[i][j]){
nx[i][j][0]=i,ny[i][j][0]=rht[i][j]-1;
lon[i][j]=min(lon[i][j],rht[i][j]-j);
}
if(dn[i][j]){
nx[i][j][3]=dn[i][j]-1,ny[i][j][3]=j;
lon[i][j]=min(lon[i][j],dn[i][j]-i);
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]==1) continue;
if(ok(i+1,j)&&(vis[i+1][j]!=1))put(i,j,i+1,j,1);
if(ok(i,j+1)&&(vis[i][j+1]!=1))put(i,j,i,j+1,1);
if(ok(i-1,j)&&(vis[i-1][j]!=1))put(i,j,i-1,j,1);
if(ok(i,j-1)&&(vis[i][j-1]!=1))put(i,j,i,j-1,1);
for(int k=0;k<4;k++){
int tmpx=nx[i][j][k],tmpy=ny[i][j][k];
if(ok(tmpx,tmpy)&&(vis[tmpx][tmpy]!=1)&&((tmpx!=i)||(tmpy!=j))){
if(tmpx==i+1&&tmpy==j) continue;
if(tmpx==i&&tmpy==j+1) continue;
if(tmpx==i-1&&tmpy==j) continue;
if(tmpx==i&&tmpy==j-1) continue;
put(i,j,tmpx,tmpy,lon[i][j]);//
}
}
}
}
dijstra();
if(diss[tx][ty]==N*N*16) {
puts("nemoguce");
return 0;
}
printf("%d",diss[tx][ty]);
}
T2:蔡老板与公司
这
道
题
其
实
可
以
当
做
一
类
题
来
做
这道题其实可以当做一类题来做
这道题其实可以当做一类题来做
如
N
O
I
P
2019
d
a
y
1
T
2
如NOIP2019 day1 T2
如NOIP2019day1T2
本
题
记
f
[
i
]
表
示
以
i
结
尾
的
最
短
的
合
法
括
号
序
列
,
h
w
[
i
]
表
示
以
在
f
[
i
]
条
件
下
合
法
序
列
的
长
度
本题记f[i]表示以i结尾的最短的合法括号序列,hw[i]表示以在f[i]条件下合法序列的长度
本题记f[i]表示以i结尾的最短的合法括号序列,hw[i]表示以在f[i]条件下合法序列的长度
则
f
[
i
]
=
f
[
i
−
h
w
[
i
]
]
+
1
(
即
以
上
一
个
回
文
点
,
每
一
个
方
案
都
可
以
加
上
本
次
的
方
案
,
同
时
,
也
可
以
不
取
上
一
个
中
的
任
何
一
种
)
则f[i]=f[i-hw[i]]+1(即以上一个回文点,每一个方案都可以加上本次的方案,同时,也可以不取上一个中的任何一种)
则f[i]=f[i−hw[i]]+1(即以上一个回文点,每一个方案都可以加上本次的方案,同时,也可以不取上一个中的任何一种)
注
意
点
:
\color{blue}注意点:
注意点:
只
有
h
w
[
i
]
不
为
0
时
才
可
转
移
\color{blue}只有hw[i]不为0时才可转移
只有hw[i]不为0时才可转移
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
char s[N];
int n,hw[N],f[N];
ll ans=0;
int main(){
scanf("%s",(s+1));
s[0]='#';
n=strlen(s+1);
for(int i=1;i<=n;i++){
int j=i-1;
while(hw[j]&&s[j]!=s[i]) j=j-hw[j];
if(s[j]==s[i]) hw[i]=i-j+1;
if(hw[i]) f[i]=f[i-hw[i]]+1;
ans=1ll*(ans+f[i]);
}
printf("%lld",ans);
}
T3:小D与子序列
大
概
是
这
种
题
的
套
路
:
大概是这种题的套路:
大概是这种题的套路:
先
想
,
能
不
能
二
分
,
但
发
现
不
可
做
,
因
为
无
法
判
断
那
些
需
要
加
那
些
不
用
加
先想,能不能二分,但发现不可做,因为无法判断那些需要加那些不用加
先想,能不能二分,但发现不可做,因为无法判断那些需要加那些不用加
观
察
一
下
数
据
发
现
可
以
O
(
n
2
)
观察一下数据发现可以O(n^2)
观察一下数据发现可以O(n2)
所
以
很
快
就
想
到
了
d
p
,
由
于
要
为
m
,
似
乎
是
背
包
,
所
以
d
p
[
j
]
表
示
和
为
j
的
最
小
选
的
数
所以很快就想到了dp,由于要为m,似乎是背包,所以dp[j]表示和为j的最小选的数
所以很快就想到了dp,由于要为m,似乎是背包,所以dp[j]表示和为j的最小选的数
d
p
数
组
修
改
的
时
候
还
要
顺
便
该
极
差
,
所
以
用
v
e
c
t
o
r
来
维
护
,
这
样
时
间
复
杂
度
是
O
(
n
3
)
,
但
是
实
际
远
远
达
不
到
这
个
上
界
,
因
为
如
果
a
[
i
]
很
小
,
栈
的
长
度
就
会
比
较
短
,
反
之
,
合
法
的
d
p
[
j
]
的
j
就
比
较
大
,
所
以
可
以
过
dp数组修改的时候还要顺便该极差,所以用vector来维护,这样时间复杂度是O(n^3),但是实际远远达不到这个上界,因为如果a[i]很小,栈的长度就会比较短,反之,合法的dp[j]的j就比较大,所以可以过
dp数组修改的时候还要顺便该极差,所以用vector来维护,这样时间复杂度是O(n3),但是实际远远达不到这个上界,因为如果a[i]很小,栈的长度就会比较短,反之,合法的dp[j]的j就比较大,所以可以过
注
意
点
:
\color{brown}注意点:
注意点:
- vector的使用,还是应该用iterator调用
- 当len=0时,front函数会炸,所以要先判断front
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int n,m,a[N],dp[N],g[N];
vector <int> ss[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
for(int i=0;i<=m;i++){
dp[i]=N;
}
for(int j=0;j<=m;j++){
ss[j].clear();
}
memset(g,0x7f,sizeof(g));
dp[0]=0;
for(int i=1;i<=n;i++){
for(int j=m;j>=1;j--){
if(j>=a[i]){
if(dp[j-a[i]]==N) continue;
if(dp[j]>dp[j-a[i]]+1){
dp[j]=dp[j-a[i]]+1;
ss[j].clear();;
int len=ss[j-a[i]].size();
int fir;
if(len)//
{
vector<int>::iterator it;
fir=ss[j-a[i]].front();
for(it=ss[j-a[i]].begin();it<ss[j-a[i]].end();it++){
ss[j].push_back(*it);
}
}
ss[j].push_back(a[i]);
if(len) g[j]=a[i]-fir;
else g[j]=0;
}
if(dp[j]==dp[j-a[i]]+1){
int len=ss[j-a[i]].size();
if(len)
{
int fir=ss[j-a[i]].front(),end=ss[j-a[i]].back();
if(a[i]-fir<g[j]){//
g[j]=a[i]-fir;
ss[j].clear();
vector <int> ::iterator it;
for(it=ss[j-a[i]].begin();it<ss[j-a[i]].end();it++)
{
ss[j].push_back(*it);
}
ss[j].push_back(a[i]);
}
}
else{
ss[j].push_back(a[i]);
g[j]=0;
}
}
}
else break;
}
}
if(dp[m]==N){
puts("-1");
return 0;
}
printf("%d",g[m]);
}
改
进
:
改进:
改进:
上
述
算
法
的
瓶
颈
在
于
用
v
e
c
t
o
r
,
但
我
们
发
现
栈
尾
的
总
是
a
[
i
]
,
所
以
其
实
我
们
只
要
记
录
栈
首
就
行
了
上述算法的瓶颈在于用vector,但我们发现栈尾的总是a[i],所以其实我们只要记录栈首就行了
上述算法的瓶颈在于用vector,但我们发现栈尾的总是a[i],所以其实我们只要记录栈首就行了
g
[
i
]
表
示
满
足
d
p
[
i
]
时
的
最
小
极
差
时
的
最
小
值
,
f
[
i
]
表
示
满
足
d
p
[
i
]
的
极
差
g[i]表示满足dp[i]时的最小极差时的最小值,f[i]表示满足dp[i]的极差
g[i]表示满足dp[i]时的最小极差时的最小值,f[i]表示满足dp[i]的极差
则
随
便
转
移
即
可
则随便转移即可
则随便转移即可
注
意
点
:
\color{blue}注意点:
注意点:
- 当a[i]时应直接存入数组,这样才能赋初始值
- 最好用f数组存答案,不要单独记录m的答案(我也不知道为什么,不这样会炸)
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int n,m;
int a[N],dp[N],g[N],f[N];
int ans=N,anss=N;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
for(int i=1;i<=m;i++){
dp[i]=N;
f[i]=N;//
g[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=m;j>=1;j--){
if(j-a[i]<=0){break;}
if(dp[j-a[i]]!=N)
{
if(dp[j]>dp[j-a[i]]+1)
{
dp[j]=dp[j-a[i]]+1;
g[j]=g[j-a[i]];
f[j]=a[i]-g[j];
}
if(dp[j]==dp[j-a[i]]+1)
{
if(a[i]-g[j]>a[i]-g[j-a[i]]){
g[j]=g[j-a[i]];
f[j]=a[i]-g[j];
}
}
}
}
dp[a[i]]=1,g[a[i]]=a[i],f[a[i]]=0;//
if(a[i]==m) {
ans=0;
break;
}
}
if(dp[m]==N){
puts("-1");
return 0;
}
printf("%d",f[m]);
}