CSP/NOIP新赛制内部挑战赛1 C. 生存

 

 

 

 

 

 

sub1

由于一个只有一个幸存者,直接dfs即可

时间复杂度:O(2^N)

期望得分;20pts

注意:考虑停留原地不动的情况! WA了一发

 

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,m,head[maxn],cnt,a[maxn];
vector <int> ds[maxn];
struct edge
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].nxt=head[x];
    head[x]=cnt;
}
int ans=0;
void dfs(int x,int step,int num)
{
    if(!num) return;
    if(step==m)
    {
        ans=max(ans,num);
        return;
    }
    int to=x;
    int flag=0;
    for(int k=0;k<ds[step+1].size();k++)
        if(ds[step+1][k]==to) flag=1;
    dfs(to,step+1,num-flag);
    for(int i=head[x];i;i=e[i].nxt)
    {
        to=e[i].to;
        flag=0;
        for(int k=0;k<ds[step+1].size();k++)
            if(ds[step+1][k]==to) flag=1;
        dfs(to,step+1,num-flag);
    }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y,pos;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        if(a[i]) pos=i;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        for(int j=1;j<=x;j++)
        {
            scanf("%d",&y);
            ds[i].push_back(y);
        }
    }
    dfs(pos,0,1);
    printf("%d",ans);
    return 0;
}

 

sub2

人与人之间不会互相影响,而且一组的人可以当作总是一起的

考虑dp 设计状态 f[x][i][j]表示第x组人在第i天能否安全到达j

转移即可 时间复杂度 O(N^2*M)

期望得分:33pts

 

代码

#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
const int maxn=1e6+5;
int n,m,head[maxn],cnt,a[maxn];
int dp[205][205];
vector <int> ds[maxn];
struct edge
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].nxt=head[x];
    head[x]=cnt;
}
int ans=0;
queue <PII> q;
void solve()
{
    for(int k=1;k<=n;k++)
    {
        memset(dp,0,sizeof(dp));
        q.push(make_pair(0,k));
        dp[0][k]=1;
        while(!q.empty())
        {
            PII u=q.front(); q.pop();
            int to=u.second;
            int tmp=1;
            for(int j=0;j<ds[u.first+1].size();j++)
                if(ds[u.first+1][j]==to) tmp=0;
            if(tmp)
            {
                dp[to][u.first+1]=1;
                if(u.first+1<m)
                    q.push(make_pair(u.first+1,to));
            }
            for(int i=head[u.second];i;i=e[i].nxt)
            {
                to=e[i].to;
                tmp=1;
                for(int j=0;j<ds[u.first+1].size();j++)
                    if(ds[u.first+1][j]==to) tmp=0;
                if(!tmp) continue;
                dp[to][u.first+1]=1;
                if(u.first+1<m)
                    q.push(make_pair(u.first+1,to));
            }
        }
        int flag=1;
        for(int i=1;i<=n;i++)
            if(dp[i][m])
            {
                flag=0;
                break;
            }
        if(!flag) ans+=a[k];
    }    
    printf("%d\n",ans);
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        for(int j=1;j<=x;j++)
        {
            scanf("%d",&y);
            ds[i].push_back(y);
        }
    }
    solve();
    return 0;
}

 

sub3

考虑到上一种dp过程中有很多重复计数

每组人在某一时刻,在某一城市最终能否安全存活是固定的,而我们却反复计算了

所以,换一种状态设计的方式

记录f[i][j]表示第i天在j位置最终能否安全

由于最后一天到达哪个城市都算安全

我们就从时间m-1到1都跑一遍即可

时间复杂度O(NM)

期望得分:52pts

 

代码

#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
const int maxn=1e6+5;
int n,m,head[maxn],cnt,a[maxn];
int dp[3005][3005];
struct edge
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].nxt=head[x];
    head[x]=cnt;
}
int ans=0;
void dfs(int t,int u,int fa)
{
    int res=0;
    if(dp[t+1][u]==1 || dp[t+1][fa]==1) res=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa) continue;
        if(dp[t+1][to]==1) res=1;
        dfs(t,to,u);
    }
    if(!dp[t][u]) return;
    dp[t][u]=res; 
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        for(int j=1;j<=x;j++)
        {
            scanf("%d",&y);
            dp[i][y]=0;
        }
    }
    for(int i=1;i<=n;i++)
        if(dp[m][i]==-1)
            dp[m][i]=1;
    for(int i=m-1;i>=0;i--)
        dfs(i,1,0);
    for(int i=1;i<=n;i++)
        ans+=(dp[0][i]==1?a[i]:0);
    printf("%d",ans);
    return 0;
}

 

sub4

继续考虑,由于上一种方法的每个点仅仅记录了0/1即为能否生存的状态

所以我们可以考虑把每个点分为黑白,黑色是不能,白色是可以存活

首先,在我们读入完第m天哪些城市没有灾难,这时,我们就得到了一些白点

那么上一种方法中的状态转移就相当于把一些黑点变成了白点

当一个黑点有相邻点是白点,那就可以把它改成白色

 

那么维护一个黑点相邻的白点有多少个,维护一个队列,存放可能有黑变白的点,在每一天结束的时候,判断哪些点变白

时间复杂度 O(k*max(入度))

期望得分:78pts

 

由于这个方法和最后的标算很相近了,就不写了

 

标算

考虑在维护每个点相邻节点入队的时候,时间复杂度带了一个度数

现在就是要考虑把这一部分省略掉

 

把儿子节点和父亲节点分开

维护白色儿子节点的个数是O(1)的,再记录一个队列,维护的节点是它们的儿子可以由黑变白

在每一天结束的时候,判断哪些点变白,然后重新维护上述两个队列

 

代码

#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
const int maxn=1e6+5;
int n,m,head[maxn],cnt,a[maxn];
vector <int> ds[maxn],sw[maxn],s1,s2;
struct edge
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].nxt=head[x];
    head[x]=cnt;
}
int f[maxn],siz[maxn],vis[maxn];
void dfs(int u,int fa)
{
    f[u]=fa;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa) continue;
        dfs(to,u);
        siz[u]++;
    }
}
int calc(int u)
{
    if(f[u]) return vis[f[u]]+siz[u];
    return siz[u];
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),vis[i]=1;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        for(int j=1;j<=x;j++)
        {
            scanf("%d",&y);
            ds[i].push_back(y);
        }
    }
    dfs(1,0);
    for(int i=m;i>0;i--)
    {
        for(int j=0;j<ds[i].size();j++)
        {
            int v=ds[i][j];
            if(vis[v])
            {
                vis[v]=0;
                if(f[v])
                {
                    --siz[f[v]];
                    sw[f[v]].push_back(v);
                }
            }
        }
        vector <int> cur;
        for(int j=0;j<ds[i].size();j++)
        {
            int v=ds[i][j];
            if(!vis[v] && calc(v))
                cur.push_back(v);
        }
        for(int j=0;j<s1.size();j++) 
        {
            int v=s1[j]; 
            if(!vis[v] && calc(v))
                cur.push_back(v); 
        }
        for(int j=0;j<s2.size();j++)
        { 
            int u=s2[j]; 
            if(vis[u]) 
            {
                for(int k=0;k<sw[u].size();k++) 
                {
                    int v=sw[u][k]; 
                    if(!vis[v])
                        cur.push_back(v); 
                }
                sw[u].clear(); 
            }
        }
        s1.clear(); 
        s2.clear();         
        for(int j=0;j<cur.size();j++) 
        {
            int v=cur[j]; 
            if(vis[v]) continue; 
            vis[v]=1; 
            if(f[v]) 
            {
                ++siz[f[v]]; 
                if(!vis[f[v]])
                    s1.push_back(f[v]); 
            }
            s2.push_back(v); 
        }
    }
    long long ans=0; 
    for(int i=1;i<=n;i++)
        if(vis[i])
            ans+=a[i];
    printf("%lld",ans);
    return 0;
}

 

posted @ 2020-11-26 16:04  andyc_03  阅读(105)  评论(0编辑  收藏  举报