NOIP2015题解

  这是一份做完题一万年以后写的题解。。

Day 1 T1 神奇的幻方

  无脑模拟题。。复杂度 O(N 2 ) 

#include<bits/stdc++.h> 
int main()
{
    int x,y,n,i,magic[39][39]={0};
    scanf("%d",&n);
    x=0;
    y=(n-1)/2;
    for(i=1;i<n*n;i++)
    {
        magic[x][y]=i;
        if (x==0)
        {
            if (y==n-1)
                x++;
            else
                x=n-1,y++;
        }
        else
        {
            if (y==n-1)
                x--,y=0;
            else
                if (magic[x-1][y+1]==0)
                    x--,y++;
                else
                    x++;
        }
    }
    magic[x][y]=n*n;
    for (x=0;x<n;x++)
    {
        for (y=0;y<=n-1;y++)
            printf("%d ",magic[x][y]);
        printf("\n");
    }
    return 0;
} 

Day 1 T2 信息传递

  题意:给定一个每个点出度都为1的有向图,无重边和自环,求图的最小环。这道题直接用 Tarjan  求强连通分量就好了。因为保证每个点出度都是1,显然每个强连通分量必然不是一个单独的点就是一个环,最后求最小的大于1的环即可。最后要扩栈。复杂度 O(N) 

#include<bits/stdc++.h>
const int N=200001; 
int t[N],dfn[N],low[N],stack[N],index=0,top=0,min=N;
bool instack[N];
void tarjan(int i)
{
    int j,num;
    instack[i]=true;
    stack[++top]=i;
    dfn[i]=++index;
    low[i]=index;
    j=t[i];
    if (!dfn[j])
    {
        tarjan(j);
        if(low[j]<low[i])
            low[i]=low[j];
    }
    else
    {
        if(instack[j] && dfn[j]<low[i])
            low[i]=dfn[j];
    }
    if (dfn[i]==low[i])
    {
        num=0;
        do
        {
            j=stack[top--];
            instack[j]=false;
            num++;  
        }
        while (j!=i);
        if (num<min && num>1)
            min=num;
    }
}
void main_main()
{
    int i=0,n;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    scanf("%d",&n);
    for (i=1;i<=n;i++)
        scanf("%d",&t[i]);
    for (i=1;i<=n;i++)
        if (!dfn[i])
            tarjan(i);
    printf("%d",min);
}
const int main_stack=16;  
char my_stack[128<<20];  
int main()
{  
  __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");  
  __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");  
  main_main();  
  __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");  
  return 0;  
}

Day 1 T3 斗地主

  这道题用记忆化搜索+状压,注意优先出掉组合牌,单牌最后一次出完,可以减少搜索次数。具体实现的时候可以把组合牌事先枚举出来预处理,然后暴力的基础上稍微优化一下就能过了。复杂度好复杂。。没法算。

#include<bits/stdc++.h>
using namespace std;
map <long long,int> dict;
int four[7],three[9],single[9],ddouble[11],tripple[7];
long long state(int *card)
{
    int i;
    long long sum=0;
    for (i=0;i<=13;i++)
    {
        sum=sum*5+card[i];
    }
    return sum;
}
int minimum(int *card)
{
    int i,j,k,min=23,time=0;
    long long st;
    bool flag=false;
    st=state(card);
    if (st==0)
        return 0;
    if (dict.count(st))
        return dict[st];
    for (i=1;i<=four[0];i++)
        if (card[four[i]]==4)
        {
            card[four[i]]-=4;
            time=minimum(card)+1;
            if (time<min)
                min=time;
            flag=true;
            for (j=0;j<=13;j++)
                if (card[j])
                {
                    card[j]--;
                    for (k=0;k<=13;k++)
                        if (card[k])
                        {
                            card[k]--;
                            time=minimum(card)+1;
                            if (time<min)
                                min=time;
                            if (card[j]&&card[k])
                            {
                                card[j]--;
                                card[k]--;
                                time=minimum(card)+1;
                                if (time<min)
                                    min=time;
                                card[j]++;
                                card[k]++;
                            }
                            card[k]++;
                        }
                    card[j]++;
                }
            card[four[i]]+=4;
        }
    for (i=1;i<=three[0];i++)
        if (card[three[i]]>=3)
        {
            card[three[i]]-=3;
            for (j=0;j<=13;j++)
                if (card[j])
                {
                    card[j]--;
                    time=minimum(card)+1;
                    if (time<min)
                        min=time;
                    flag=true;
                    if (card[j])
                    {
                        card[j]--;
                        time=minimum(card)+1;
                        if (time<min)
                            min=time;
                        card[j]++;
                    }
                    card[j]++;
                }
            card[three[i]]+=3;
        }
    for (i=1;i<=single[0];i++)
        if (card[single[i]]&&card[single[i]+1]&&card[single[i]+2]&&card[single[i]+3]&&card[single[i]+4])
        {
            card[single[i]]--;
            card[single[i]+1]--;
            card[single[i]+2]--;
            card[single[i]+3]--;
            card[single[i]+4]--;
            time=minimum(card)+1;
            if (time<min)
                min=time;
            for (j=single[i]+5;j<=11;j++)
                if (card[j])
                {
                    card[j]--;
                    time=minimum(card)+1;
                    if (time<min)
                        min=time;
                }
                else
                {
                    card[j]--;
                    break;
                }
            flag=true;
            if (j>=12)
                j=11;
            for (k=single[i];k<=j;k++)
                card[k]++;
        }
    for (i=1;i<=ddouble[0];i++)
        if (card[ddouble[i]]>=2&&card[ddouble[i]+1]>=2&&card[ddouble[i]+2]>=2)
        {
            card[ddouble[i]]-=2;
            card[ddouble[i]+1]-=2;
            card[ddouble[i]+2]-=2;
            time=minimum(card)+1;
            if (time<min)
                min=time;
            for (j=ddouble[i]+3;j<=11;j++)
                if (card[j]>=2)
                {
                    card[j]-=2;
                    time=minimum(card)+1;
                    if (time<min)
                        min=time;
                }
                else
                {
                    card[j]-=2;
                    break;
                }
            flag=true;
            if (j>=12)
                j=11;
            for (k=ddouble[i];k<=j;k++)
                card[k]+=2;
        }
    for (i=1;i<=tripple[0];i++)
        if (card[tripple[i]]>=3&&card[tripple[i]+1]>=3)
        {
            card[tripple[i]]-=3;
            card[tripple[i]+1]-=3;
            time=minimum(card)+1;
            if (time<min)
                min=time;
            for (j=tripple[i]+2;j<=11;j++)
                if (card[j]>=3)
                {
                    card[j]-=3;
                    time=minimum(card)+1;
                    if (time<min)
                        min=time;
                }
                else
                {
                    card[j]-=3;
                    break;
                }
            flag=true;
            if (j>=12)
                j=11;
            for (k=tripple[i];k<=j;k++)
                card[k]+=3;
        }
    if (!flag)
    {
        for (i=0;i<=13;i++)
            if(card[i])
                time++;
        if (time<min)
            min=time;
    }
    dict[st]=min;
    return min;
}
int main()
{
    int card[14],a,b,t,n,i,j,ans[100];
    scanf("%d",&t);
    scanf("%d",&n);
    for (i=0;i<t;i++)
    {
        memset(card,0,14*sizeof(int));
        for (j=0;j<n;j++)
        {
            scanf("%d%d",&a,&b);
            switch (a)
            {
                case 0:card[13]++;break;
                case 1:case 2:card[a+10]++;break;
                default:card[a-3]++;break;
            }
        }
        four[0]=0;
        three[0]=0;
        single[0]=0;
        ddouble[0]=0;
        tripple[0]=0;
        for (j=0;j<=12;j++)
        {
            if (card[j]==4)
                four[++four[0]]=j;
            if (card[j]>=3)
                three[++three[0]]=j;
        }
        for (j=0;j<=7;j++)
            if (card[j]&&card[j+1]&&card[j+2]&&card[j+3]&&card[j+4])
                single[++single[0]]=j;
        for (j=0;j<=9;j++)
            if (card[j]>=2&&card[j+1]>=2&&card[j+2]>=2)
                ddouble[++ddouble[0]]=j;
        for (j=0;j<=10;j++)
            if (card[i]>=3&&card[i+1]>=3)
                tripple[++tripple[0]]=j;
        ans[i]=minimum(card);
    }
    for (i=0;i<t;i++)
        printf("%d\n",ans[i]);
    return 0;
}

Day 2 T1 跳石头

  这是一道典型的最大化最小值的题,可以用二分答案来做。判断答案是否可行时可以用贪心的思想来做,从左到右扫时,距离小于答案的石子一定要先拿掉(否则最小值一定会小于答案),所以只要从左到右不断拿掉这样的石子看次数是否满足要求即可。注意终点的石头不能拿走,要特殊处理。复杂度 O(NlogN) 

#include<bits/stdc++.h>
const int N=50001; 
int main()
{
    int l,n,m,i,d[N],d1[N],d2[N],left=0,right,mid,time;
    bool flag;
    scanf("%d%d%d",&l,&n,&m);
    right=l;
    for (i=0;i<n;i++)
    {
        scanf("%d",&d[i]);
    }
    d1[0]=d2[0]=d[0];
    for (i=1;i<=n-1;i++)
        d1[i]=d2[i]=d[i]-d[i-1];
    d1[n]=d2[n]=l-d[n-1];
    while (left<right-1)
    {
        mid=(left+right)/2;
        flag=true;
        time=m;
        for (i=0;i<n;i++)
        {
            if (d1[i]<mid)
            {
                d1[i+1]+=d1[i];
                time--;
            }
            if (time<0)
            {
                flag=false;
                break;
            }
        }
        if (d1[n]<mid)
        {
            time--;
            if (time<0)
                flag=false;
        }
        if (flag)
            left=mid;
        else
            right=mid;
        for (i=0;i<=n;i++)
            d1[i]=d2[i];
    }
    if(left==right) 
        printf("%d",right);
    else
    {
        flag=true;
        time=m;
        for (i=0;i<n;i++)
        {
            if (d1[i]<right)
            {
                d1[i+1]+=d1[i];
                time--;
            }
            if (time<0)
            {
                flag=false;
                break;
            }
        }
        if (d1[n]<right)
        {
            time--;
            if (time<0)
                flag=false;
        }
        if (flag)
            printf("%d",right);
        else
            printf("%d",left);
    }
    return 0;
}

Day2 T2 子串

  DP。设 dp[i][j][k][0]  a  串中前i 个字符与 b  串中前j 个字符取出 k  个子串,且恰好用到了a 串的第 i  个字符,dp[i][j][k][1] 则表示不考虑是否用到 a  串的第i 个字符。显然有
   dp[i][j][k][1]=dp[i1][j1][k][1]+dp[i][j][k][0] 
  对于 dp[i][j][k][0]  则要分类讨论。
  

⎧ ⎩ ⎨ ⎪ ⎪ dp[i][j][k][0]=0                                                               (a[i]==b[j])dp[i][j][k][0]=dp[i1][j1][k][1]+dp[i1][j1][k1][0](a[i]!=b[j])  

  最后要注意会MLE,所以要用个滚动数组。复杂度 O(nmk) 

#include<bits/stdc++.h>
#define N (1001)
#define M (201)
#define K (201)
#define mod (1000000007)
int f[2][M][K][2],i,j,k,n,m,kk,pre=0,now=1;
char a[N],b[M];
int main()
{
    memset(f,0,2*M*K*2*sizeof(int));
    scanf("%d%d%d\n%s\n%s",&n,&m,&kk,a,b);
    f[0][0][0][1]=1;
    f[1][0][0][1]=1;
    for (i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++)
            for(k=1;k<=kk;k++)
            {
                if (a[i-1]==b[j-1])
                    f[now][j][k][0]=(f[pre][j-1][k][0]+f[pre][j-1][k-1][1])%mod;
                else
                    f[now][j][k][0]=0;
                f[now][j][k][1]=(f[now][j][k][0]+f[pre][j][k][1])%mod;
            }
        now=now^pre;
        pre=now^pre;
        now=now^pre;
    }
    printf("%d\n",f[pre][m][kk][1]);
    return 0;
}

Day2 T3 运输计划

  这是一道最小化最大值的题,首先想到二分答案。但是,二分答案以后判断可行性却比较复杂。考虑把所有长度大于答案的路径枚举出来,求出它们边的集,若没有边重叠了 n  次,显然这个答案是不可行的。否则删去最大的这样的边,如果最长路径仍大于答案,则不可行,否则可行。具体实现时,两点之前的最短路径用LCA 来求, LCA  可以用树链剖分来解决,而统计边的重叠的次数可以用前缀和的方法来做。复杂度 O(nlogn) 

#include<bits/stdc++.h>
#define N 300001
typedef struct edge
{
    int length,next,to,cnt;
};
typedef struct point
{
    int dep,first,fa,siz,son,dis,top,cnt;
};
edge edg[2*N];
point poi[N];
int e=0,max=0,L,LCA[N],u[N],v[N],dis[N],m,n,a[N],b[N],t[N];
void addedg(int a,int b,int l)
{
    edg[++e].next=poi[a].first;
    edg[e].length=l;
    edg[e].to=b;
    poi[a].first=e;
    edg[++e].next=poi[b].first;
    poi[b].first=e;
    edg[e].length=l;
    edg[e].to=a;
}
void dfs(int i,int deep)
{
    int j,max1=0;
    poi[i].dep=deep;
    poi[i].siz=1;
    for (j=poi[i].first;j;j=edg[j].next)
    {
        if (poi[i].fa!=edg[j].to)
        {
            poi[edg[j].to].fa=i;
            poi[edg[j].to].dis=poi[i].dis+edg[j].length;
            dfs(edg[j].to,deep+1);
            poi[i].siz+=poi[edg[j].to].siz;
            if (poi[edg[j].to].siz>max1)
            {
                max1=poi[edg[j].to].siz;
                poi[i].son=edg[j].to;
            }
        }
    }
}
void dfs1(int i)
{
    int j;
    if (poi[poi[i].fa].son==i)
    {
        poi[i].top=poi[poi[i].fa].top;
    }
    else
        poi[i].top=i;
    for (j=poi[i].first;j;j=edg[j].next)
        if (poi[i].fa!=edg[j].to)
            dfs1(edg[j].to);
}
int lca(int a,int b)
{
    while (poi[a].top!=poi[b].top)
    {
        if (poi[poi[a].top].dep<poi[poi[b].top].dep)
        {
            a=a^b;
            b=a^b;
            a=a^b;
        }
        a=poi[poi[a].top].fa;
    }
    return poi[a].dep<poi[b].dep?a:b;
}
int dfs2(int i)
{
    int cnt=0,j;
    for(j=poi[i].first;j;j=edg[j].next)
    {
        if (poi[i].fa!=edg[j].to)
        {
            edg[j].cnt=dfs2(edg[j].to);
            poi[i].cnt+=poi[edg[j].to].cnt;
        }
    }
    return poi[i].cnt;
}
bool check(int m,int mid)
{
    int i,total=0,max1=0;
    for (i=0;i<m;i++)
    {
        if (dis[i]>mid)
        {
            poi[u[i]].cnt++;
            poi[v[i]].cnt++;
            poi[LCA[i]].cnt-=2;
            total++;
        }
    }
    dfs2(1);
    for (i=1;i<=2*n;i++)
        if (edg[i].cnt==total)
            if (edg[i].length>max1)
                max1=edg[i].length;
    return L-max1<=mid;
}
void main_main()
{
    int i,left,right,mid;
    memset(edg,0,2*N*sizeof(edge));
    memset(poi,0,N*sizeof(point));
    scanf("%d%d",&n,&m);
    for (i=0;i<n-1;i++)
    {
        scanf("%d%d%d",&a[i],&b[i],&t[i]);
        addedg(a[i],b[i],t[i]);
        if (t[i]>max)
            max=t[i];
    }
    for (i=0;i<m;i++)
        scanf("%d%d",&u[i],&v[i]);
    dfs(1,1);
    dfs1(1);
    for (i=0;i<m;i++)
    {
        dis[i]=poi[u[i]].dis+poi[v[i]].dis-2*poi[LCA[i]=lca(u[i],v[i])].dis;
        if (L<dis[i])
            L=dis[i];
    }
    left=(L-max)>0?L-max:0;
    right=L;
    while(left<right)
    {
        for(i=1;i<=n;i++)
            poi[i].cnt=0;
        for (i=1;i<=2*n;i++)
            edg[i].cnt=0;
        mid=(left+right)/2;
        if (check(m,mid))
            right=mid;
        else
            left=mid+1;
    }
    printf("%d",left);
}
const int main_stack=16;  
char my_stack[128<<20];  
int main()
{  
  __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");  
  __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");  
  main_main();  
  __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");  
  return 0;  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值