首页 > 代码库 > 【NOIP2003】提高组

【NOIP2003】提高组

T1神经网络

题目链接

这道题是第一题,把它放在签到题的位置就不用想太多。听说可以用拓扑排序,但是实际上由于数据比较水,裸的bfs即可AC。

注意几点:输入层的ci[i]不需要减去ui[i];只有当ci[i]>0时才能传递给下一层;只需要输出ci[i]>0的输出层。

剩下的细节实现看代码:

技术分享
#include<cstdio>
#include<cstring>
const int maxn=205;
using namespace std;
int ci[maxn],ui[maxn],q1[maxn],q2[maxn],a1=0,a2=0;
int u,v[maxn],s[maxn],first[maxn],next[maxn];
bool ok[maxn];
int read()
{
    int ans=0,f=1;char c=getchar();
    while(c<0||c>9){if(c==-)f=-1;c=getchar();}
    while(c>=0&&c<=9){ans=ans*10+c-48;c=getchar();}
    return ans*f;
}
void bfs()
{
    int head=0,to;
    while(head<a1)
    {
        int x=q1[++head];
        for(int i=first[x];i>0;i=next[i])
        {
            to=v[i];
            ci[to]+=s[i]*ci[x];
            if(ok[to]&&ci[to]>0)q1[++a1]=to,ok[to]=0;
        }
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    int n=read(),p=read();
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&ci[i],&ui[i]);
        if(ci[i]>0)q1[++a1]=i;
        else ci[i]-=ui[i];
    }
    for(int i=1;i<=p;i++)
    {
        scanf("%d %d %d",&u,&v[i],&s[i]);
        next[i]=first[u];first[u]=i;
        ok[u]=1;
    }
    for(int i=1;i<=n;i++)if(!ok[i])q2[++a2]=i;
    bfs();bool f=0;
    for(int i=1;i<=a2;i++)
    {
        int x=q2[i];
        if(ci[x]>0)
        {
            f=1;printf("%d %d\n",x,ci[x]);
        }
    }
    if(!f)printf("NULL");
    return 0;
}
T1

T2侦探推理

题目链接

这道题是字符串的模拟题,很恶心......我就不写了,有兴趣的自行跳转到此链接

 

T3加分二叉树

题目链接

这道题就是一个套在树上的dp,枚举每一个点为根节点的情况,递归更新答案即可。

注:p[l][r]表示[l~r]这一段的根节点。

具体细节看代码:

技术分享
#include<cstdio>
#include<cstring>
const int maxn=35;
typedef long long LL; 
using namespace std;
LL ans=0,f[maxn][maxn],w[maxn];
int n,p[maxn][maxn];
int read()
{
    int ans=0,f1=1;char c=getchar();
    while(c<0||c>9){if(c==-)f1=-1;c=getchar();}
    while(c>=0&&c<=9){ans=ans*10+c-48;c=getchar();}
    return ans*f1;
}
LL dp(int l,int r)
{
    if(l>r)return 1;
    if(f[l][r]!=-1)return f[l][r];
    if(l==r){p[l][r]=l;return w[l];}
    for(int i=l;i<=r;i++)
    {
        LL x=dp(l,i-1)*dp(i+1,r)+w[i];
        if(x>f[l][r])
        {
            f[l][r]=x;
            p[l][r]=i;
        }    
    }
    return f[l][r];
}
void print(int l,int r)
{
    int x=p[l][r];
    printf("%d ",x);
    if(l<x)print(l,p[l][r]-1);
    if(r>x)print(p[l][r]+1,r);
}
int main()
{
    memset(f,-1,sizeof(f));
    n=read();
    for(int i=1;i<=n;i++)
        w[i]=read();
    ans=dp(1,n);
    printf("%lld\n",ans);
    print(1,n);
    return 0;
}
T3

 T4传染病控制

题目链接

这道题告诉了我们它是一棵树,因此可以直接连一条有向边。

刚开始自己的DFS总是写炸,但是有些数据是真的水错误的程序都能水过6个点2333。

最后参考了一下某位dalao的代码才恍然大悟,我在细节处理上总是差那么一点点(还是要努力啊)。

DFS的话枚举当前到下一步能走到的所有点中被断绝联系的那一个点,然后再将除了这个点外其他点的子节点都存进son数组里,如果存完son数组仍为空,则此时已经控制住,可以尝试更新答案。

还可以加一个小小的剪枝:如果当前情况的num已经大于我们目前的最小值,则这个答案肯定不是最优解,不必继续往下搜,直接跳出。

剩下的直接递归DFS即可(不得不说函数传一整个数组真是方便get√)。

具体细节还是看代码:

技术分享
#include<cstdio>
#include<iostream>
#include<cstring>
const int maxn=310,inf=0x3f3f3f3f;
using namespace std;
int n,p,u,v,to[maxn],tot=0,mni=inf;
int first[maxn],xia[maxn];
int read()
{
    int ans=0,f=1;char c=getchar();
    while(c<0||c>9){if(c==-)f=-1;c=getchar();}
    while(c>=0&&c<=9){ans=ans*10+c-48;c=getchar();}
    return ans*f;
}
void insert()
{
    tot++;to[tot]=v;xia[tot]=first[u];first[u]=tot;
}
void dfs(int tr[maxn],int sz,int num,int d)
{
    int son[maxn],td=0;
    for(int i=1;i<=sz;i++)
    {
        int x=tr[i];
        if(x==d)continue;
        for(int j=first[x];j>0;j=xia[j])
            son[++td]=to[j];
    }
    if(!td)mni=min(mni,num);
    else{
        if(num+td-1>=mni)return;
        for(int i=1;i<=td;i++)
            dfs(son,td,num+td-1,son[i]);
    }
} 
int main()
{
    memset(first,-1,sizeof(first));
    int son[maxn];
    n=read();p=read();int td=0;
    for(int i=1;i<=p;i++)
    {
        u=read();v=read();
        if(u>v)swap(u,v);
        insert();
        if(u==1)son[++td]=v;
    }
    for(int i=1;i<=td;i++)
    dfs(son,td,td,son[i]);
    printf("%d",mni);
    return 0;
}
T4

 

【NOIP2003】提高组