码迷,mamicode.com
首页 > 其他好文 > 详细

练习赛41

时间:2019-07-15 15:57:51      阅读:123      评论:0      收藏:0      [点我收藏+]

标签:pac   需要   std   题意   计算   通过   last   read   hdu   

练习赛41

Begin:
2019-03-27 18:35

PDD:310rlb

A

递增N元组 HihoCoder-1733

标签:dp计数(55)

题目大意:

给定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

标签:Spfa+yy优化(75)

题目大意:

给定一张地图,‘.‘代表可以走,‘#‘代表不能走,但可以通过施膜法通过一个‘#‘,膜法只能用一次,问从(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

标签:万恶的单调队列 凸(艹皿艹 ) (80)

题目大意:

给出一个长度为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

标签:模拟(100)

题目大意:

小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

标签:pac   需要   std   题意   计算   通过   last   read   hdu   

原文地址:https://www.cnblogs.com/Tieechal/p/11189357.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!