练习赛41
Posted tieechal
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了练习赛41相关的知识,希望对你有一定的参考价值。
练习赛41
Begin:
2019-03-27 18:35
PDD:310rlb
A
递增N元组 HihoCoder-1733
题目大意:
给定N个数组,每个数组都包含M个整数。
现在你被要求从每个数组中选出一个数,总共N个数。
在 M^N 种选法中,有多少种选法满足选出的N个数恰好是严格递增的?
CODE:
#include<bits/stdc++.h>
using namespace std;
#define oo 1000000007
#define M 10009
int a[110][M],pos[110][M],n,m;
int dp[110][M],res[M],ans;
int main()
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
sort(a[i]+1,a[i]+m+1);
for(int i=2;i<=n;i++)
int x=m;
for(int j=m;j>=1;j--)
while(a[i-1][x]>=a[i][j]&&x>=1)x--;
pos[i][j]=x;
for(int i=1;i<=m;i++)dp[1][i]=1;
for(int i=2;i<=n;i++)
res[1]=dp[i-1][1];
for(int j=2;j<=m;j++)res[j]=(res[j-1]+dp[i-1][j])%oo;
for(int j=1;j<=m;j++)if(pos[i][j]>=0)dp[i][j]=res[pos[i][j]];
for(int i=1;i<=m;i++)ans=(ans+dp[n][i])%oo;
printf("%d",ans);
B
逃离迷宫5 HihoCoder - 1734
题目大意:
给定一张地图,‘.‘代表可以走,‘#‘代表不能走,但可以通过施膜法通过一个‘#‘,膜法只能用一次,问从(1,1)走到(n,n)的最短长度。
思路:
这道题暴力dfsT了,转变一下思路,枚举使用膜法的块(假设对于一个点也可以施膜法,即什么也不改变):从(1,1)spfa一次,只将‘.‘的块加入队列中;再从(n,n)spfa一次,同样也只将‘.‘的块加入队列中。那么ans=max(ans,dis1[i]+dis2[i])。跑了74ms,竟然是VJ上最快的
CODE:
#include<bits/stdc++.h>
#define oo 10000000
const int N=1010;
using namespace std;
struct node
int x,y;
q[N*N];
bool vis[N][N];
int n,dis[N][N],hd,tl,dis2[N][N];
const int dx[4]=0,0,1,-1;
const int dy[4]=1,-1,0,0;
char c[N][N];
void spfa(int x,int y,bool f)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)dis[i][j]=oo,vis[i][j]=0;
hd=tl=1;
q[1]=(node)x,y,dis[x][y]=0,vis[x][y]=1;
while(hd<=tl)
node t=q[hd];
int d=dis[t.x][t.y];
for(int i=0;i<=3;i++)
int xx=t.x+dx[i],yy=t.y+dy[i];
if(xx<=0||xx>n||yy<=0||yy>n)continue;
if(dis[xx][yy]>d+1)
dis[xx][yy]=d+1;
if(!vis[xx][yy]&&c[xx][yy]=='.')
q[++tl]=(node)xx,yy;
vis[xx][yy]=1;
hd++;
if(f==0)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)dis2[i][j]=dis[i][j];
int main()
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%s",c[i]+1);
spfa(1,1,0),spfa(n,n,1);
int ans=oo;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)ans=min(ans,dis[i][j]+dis2[i][j]);
printf("%d",ans==oo?-1:ans);
C
Subsequence HDU - 3530
题目大意:
给出一个长度为n的数列,求一个最长的区间,使得区间中最小值和最大值的差在[m,k]之间。
思路:
第一眼,woc单调队列的既视感........好的,,,,不会写QAQ
way1:单调队列O(N)
1.来两个单调队列,一个维护最大值,一个维护最小值。
2.如果此时Ma-Mi>R,那么我们要么把Mi搞大,要么把Ma弄小,然后我们惊奇的发现,对应的操作都是将其中一个单调队列head++,即把队首元素pop出去。
3.那么到底要pop哪个呢,我们画个图就知道了,当然是pop掉位置靠前的那一个,但为什么呢:
我们来看:
choose1
如果我们pop掉Ma,并且假设接下来的Ma-Mi<=R(符合条件),但我们也不能取[i,r]这个长度作为ans,因为[i,r]中还包含着那个已经pop掉的最大值,所以我们只能取[pos[lastma]+1,r]作为答案,就是把当前区间彻底挪开上一个最大值的位置。
choose2
同理,假如我们pop的是Mi,并且假设接下来的Ma-Mi<=R(符合条件),那么我们得到的最优解
是[pos[lastmi]+1,r]。
综上,假设这次pop操作后能使当前区间符合条件,答案是pos[lastma]+1,r]或[pos[lastmi]+1,r],
要取得最优解,当然是pos[Mi]小popMi,反之popMa。
假如现在你已经使Ma-Mi<=R,如果当前Ma-mi还能满足>=L,那么就可以更新答案了。至于是r-pos[lastmi]还是r-pos[lastma]得取决你刚才最后pop的是哪个,可以用个简单的判断(见代码pos_mi,pos_ma);大概就这样了,虽然讲起来有点麻烦,但代码其实很短,需要注意一些细节。
CODE:
#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
int a[N],q1[N],q2[N];
int head1,head2,tail1,tail2,pos_ma,pos_mi,ans;
int main()
int n,L,R;
while(~scanf("%d%d%d",&n,&L,&R))
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
ans=head1=head2=tail1=tail2=0;
pos_mi=pos_ma=0;
for(int i=1;i<=n;i++)
while(head1<tail1&&a[q1[tail1-1]]<=a[i])tail1--;
q1[tail1++]=i;
while(head2<tail2&&a[q2[tail2-1]]>=a[i])tail2--;
q2[tail2++]=i;
while(a[q1[head1]]-a[q2[head2]]>R)
if(q1[head1]<q2[head2])pos_ma=q1[head1++];
else pos_mi=q2[head2++];
if(a[q1[head1]]-a[q2[head2]]>=L)
ans=max(ans,i-max(pos_ma,pos_mi));
printf("%d\n",ans);
way2:Multiset O(NlogN)
数据有点水,能过。而且代码比较短。
D
一人麻将 HihoCoder - 1503
题目大意:
小Hi起手拥有14张牌,之后小Hi每摸一张牌后,如果没有胡牌,就出一张牌,直至胡牌或牌被摸光。
胡牌的规则如下:
手中14张牌最多只能属于两个花色。
手中14张牌的牌型须满足下列两个条件之一:
1)3 + 3 + 3 + 3 + 2,其中的3代表一坎(指同花色且同数字、或同花色且数字相连的三张牌),其中的2代表一个对子(指两张同花色且同数字的牌)。
2)2 × 7,即14张牌构成7个对子,特别的,四张花色数字全相同的牌可以看作两个对子。
现给出小Hi的起手牌,并按顺序给出场上其余小Hi将要摸的牌。
请计算小Hi最早在摸第几张牌之后就可能胡牌,如果起始牌就满足条件输出0。无法胡牌输出-1。
思路:
首先要理解题意:
1.明确一点:题目说每摸张牌如果没法胡,都要弃掉一张手牌。其实我们不必考虑每轮丢哪张牌,我们可以采取上帝视角,把每张牌都保留下来,等到什么时候能胡牌了,再把那些多余的牌扔掉,这样没有任何影响吧。超级骚操作
2.胡牌有两种大方案:
A :四坎一对 B: 七对
先来看怎么判B方案:
bool ok2()
int tmp[5];
for(int i=0;i<=2;i++)
tmp[i]=0;
for(int j=1;j<=9;j++)tmp[i]+=cnt[i][j]/2;
if(tmp[0]+tmp[1]>=7)return 1;
if(tmp[0]+tmp[2]>=7)return 1;
if(tmp[1]+tmp[2]>=7)return 1;
return 0;
由代码可以看出——...什么都看不出,这一步很好想,不解释了
再来看怎么判A方案,这里有点复杂:
bool dfs(int now)//接下来要凑第几个
if(now>=5)//现在要凑最后一个————对子
for(int i=1;i<=9;i++)
if(cnt[co1][i]>=2||cnt[co2][i]>=2)return 1;
return 0;
//现在要凑————坎(smg名字????)
//坎又有两种:同花色且同数字/同花色且数字相连.
//same_num:co1
for(int i=1;i<=9;i++)
if(cnt[co1][i]>=3)
cnt[co1][i]-=3;
if(dfs(now+1))return 1;
cnt[co1][i]+=3;
//same_num:co2 同上操作,只要把co1改成co2即可
//link_num:co1
for(int i=1;i<=7;i++)
if(cnt[co1][i]&&cnt[co1][i+1]&&cnt[co1][i+2])
cnt[co1][i]--,cnt[co1][i+1]--,cnt[co1][i+2]--;
if(dfs(now+1))return 1;
cnt[co1][i]++,cnt[co1][i+1]++,cnt[co1][i+2]++;
//link_num:co2 同上操作,只要把co1改成co2即可
return 0;
3.就是这样。那么co1,co2又是什么呢,题目中还说胡牌有一个前提条件:“手中14张牌最多只能属于两个花色”(一定要好好好好看题),所以我们需要枚举还握在手中的牌的花色:最多只能有两种。
bool choosecolor()
for(co1=0;co1<=2;co1++)
for(co2=co1;co2<=2;co2++)if(dfs(1))return 1;
return 0;
4.完整代码如下,部分重要函数已于上面出示过了,这里减短码量,给一个大致框架
Code:
#include<bits/stdc++.h>
using namespace std;
int cnt[5][12];
int co1,co2,n;
char s[5];
void read()//存牌
scanf("%s",s);
cnt[s[0]-'a'][s[1]-'0']++;
bool dfs(int now)
bool choosecolor()
bool ok2()
int main()
scanf("%d",&n);
for(int i=1;i<=14;i++)read();
if(choosecolor())puts("0");return 0;
if(ok2())puts("0");return 0;
for(int i=1;i<=n;i++)
read();
if(ok2())printf("%d",i);return 0;
if(choosecolor())printf("%d",i);return 0;
return puts("-1"),0;
以上是关于练习赛41的主要内容,如果未能解决你的问题,请参考以下文章
HihoCoder1672 : 区间问题([Offer收割]编程练习赛41)(贪心)
HihoCoder1673 : 01间隔矩阵([Offer收割]编程练习赛41)(单调队列)
HiHoCoder1671 : 反转子串([Offer收割]编程练习赛41)(占位)