中山DAy2——普及

今天挺不友好的,早上忘记定闹钟,晚了半小时起床,然后早上信心满满打算弄他个300分。结果……132.2分·。WTF???


T1:disease

题意:有n头奶牛,k种细菌(k<=15),给你每头奶牛携带的细菌种类,问:若让选出所有奶牛携带细菌少于d种,最多选几头奶牛?

思路:上手就用了动归,可能是昨天打积木那题,自己信心爆棚。然后……神奇的22.2分出炉啦!  之后,我痛定思痛,发现动归真的是伪得快。

正解:反正k小,把可能情况枚举一下,最多也没几种。不就是$C_k^d$种吗?然后比较打擂台就可以了。

见代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,ans,d,k,inf=-0x3f3f3f,flag[16],use[16]; 
bool a[1001][16];
void dfs(int x)
{
    for(int i=use[x-1]+1;i<=d;i++)
    {
        if(flag[i]==0)
        {
            flag[i]=1;
            use[x]=i;
            if(x==k)
            {
                for(int j=1;j<=n;j++)
                {
                       for(int i=1;i<=d;i++)
                    {
                        if(a[j][i]==true&&flag[i]==0)
                          {
                        ans--;
                        break;
                        }
                    }
                }
                if(ans>inf)
                inf=ans;
                ans=n;
            }
            else
            dfs(x+1);    
            flag[i]=0;
            use[x]=0;
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&d,&k);
    ans=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[0][0]);
        for(int j=1;j<=a[0][0];j++)
        {
            scanf("%d",&a[0][1]);
            a[i][a[0][1]]=true;
        }
    }
    dfs(1);
    printf("%d",inf);
    return 0;
}

注意事项:

要开一个数组,依次向后走,去记录,万万不能从1到k来回搞,不然就不是$C_d^k$而是C!了。

好题哉!!!


T2: jumpcow

题意:有一个序列,你可以选择其中任意个数组成一个子序列,其中有个弹跳值,弹跳值为子序列奇数项的和乘上偶数项的和,问:弹跳值最大为多少?

思路:熟悉的操作,熟悉的味道——动归!一开始在考场上,把奇偶混在一起,没有分开奇数项和偶数项,只有10分。

正解:奇偶分开是什么意思呢?很简单。就是设一个二维数组:a[n][2],其中第二维为1表示此项做奇数项是的情况,第二维为0表示此项是偶数项的情况。

转移方程:a[n][1]=max(a[n-1][0]+a[n][1],a[n-1][1])这个意思就是:如果不选此项就是前一个奇数项,然后依次向后转移。若选此项就是前一项为偶数项那就加上。

                同理:a[n][0]=max(a[n-1][1]-a[n][0],a[n-1][0])。

神奇的O(n)算法!

见代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,max1=-0x3f3f3f,max2=-0x3f3f3f,inf=-0x3f3f3f;
int a[150001][2];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i][1]);    
    }
    for(int i=2;i<=n;i++)
    {
        max1=-0x3f3f3f;
        max2=-0x3f3f3f;
        for(int j=1;j<i;j++)
        {
            if(a[j][1]>max1)
            max1=a[j][1];
            if(a[j][0]>max2)
            max2=a[j][0];
        }
        a[i][0]=max1-a[i][1];
        a[i][1]+=max2;    
    }
    for(int i=1;i<=n;i++)
    {
        if(inf<a[i][0])
        inf=a[i][0];
        if(inf<a[i][1])
        inf=a[i][1];
    }
    printf("%d",inf);
    return 0;
}

好题哉!!!


T3:town

题意:有一个被水淹没小镇,给你小镇每块地的高度,每块地上的水可以流到上、下、左、右当中不高于它高度的格子里,可以在任何格子里安装一台水泵,水泵可以抽走无限量的水。问:最少需要安装几台水泵可以抽走所有的水?

正解:宽搜。先找到最低的格子,然后向高处拓展标记,如果没有抽完就找第二小的格子继续搜,以此类推。用一个变量记录一下次数即可。

见代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,k,head,ans,tail=1;
int h1[2501],l1[2501],p[5]={0,-1,1,0,0},y[5]={0,0,0,-1,1};
char a[51][51];
bool flag[51][51];
void bfs(int h,int l)
{ 
    head=0;
    tail=1;
    h1[1]=h;
    l1[1]=l;
    flag[h][l]=true;
    while(head!=tail)
    {
        head++;
        for(int i=1;i<=4;i++)
        {
            if(a[h1[head]+p[i]][l1[head]+y[i]]>0&&flag[h1[head]+p[i]][l1[head]+y[i]]==false&&a[h1[head]+p[i]][l1[head]+y[i]]>=a[h1[head]][l1[head]])
            {
                tail++;
                h1[tail]=h1[head]+p[i];
                l1[tail]=l1[head]+y[i];
                flag[h1[tail]][l1[tail]]=true;
            }
        }
    }
}
int main()
{
    scanf("%d",&k);
    for(int q=1;q<=k;q++)
    {
        ans=0;
        memset(a,-1,sizeof(a));
        memset(flag,false,sizeof(flag));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            cin>>a[i][j];
        }
        while(1)
        {
            bool flag2=false;
            int min=0x3f3f3f,minh=0,minl=0;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    if(a[i][j]<min&&flag[i][j]==false)
                    {
                        min=a[i][j];
                        minh=i;
                        minl=j;
                        flag2=true;
                    }
                }
            if(flag2==false)
            break;
            bfs(minh,minl);
            ans++;    
        }
        printf("%d
",ans);
    }
    return 0;
}

好题哉!!!


T4:cowtract

最大生成树模板题:没啥好讲的,博主也不会讲,上网搜呗。

见代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,ans,ans1,fa[20001],flag;
struct cow{
    int father,son,cost;
};
cow tree[20001];
bool cmp(cow x,cow y)
{
    return x.cost>y.cost;
}
int find(int x)
{
    if(fa[x]==x)
    return x;
    else
    return fa[x]=find(fa[x]);
}
void add(int x,int y)
{
    fa[fa[x]]=fa[y];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&tree[i].father,&tree[i].son,&tree[i].cost);
    }
    sort(tree+1,tree+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        if(find(tree[i].father)!=find(tree[i].son))
        {   
            ans1++;
            add(tree[i].father,tree[i].son);
            ans+=tree[i].cost;
        }    
    }
    if(ans1==n-1)
    {
        printf("%d",ans);
        return 0;
    }
    else
    printf("-1");
    return 0;
}

好题哉!!!

原文地址:https://www.cnblogs.com/qing1/p/11288394.html