2017.2.25.期末测评(?)2测试总结

序(?)

比以前好了很多,从最开始的一脸懵逼到现在渐渐会用朴(暴)素(力)的方法拼一些题了(虽然这次全挂orz),代码能力有所增强。但是模板的熟练背诵与运用还不行,这方面得注意加强。不得不说这两次期末测评(?)之所以分数看起来还行很大的原因是因为并不是靠模板的运用得分,靠想法方面偏多。
第一次70/300,第二次80/300
以下放题:


1.电影院

题目背景
SOURCE:NOIP2015-LC

题目描述
在 Nihon 这片神奇的土地上一共有着 m 个演员。其中每个演员都有着一个独一无二的编号:1 到 m 中的某个整数。Haruchinsan 只看与这 m 个演员相关的电影,并且她喜欢其中的 k 个演员。对于在下个月上映的电影,Haruchinsan 会观看电影的预告片并且记录下这些信息:电影名,演员个数,参演的演员编号。很不幸的是,在 Haruchinsan 的记录中,每部电影只有电影名和演员个数保存完好,演员编号有一部分看不清楚。对于某部电影,如果没有其它的电影包含比这部电影更多的 Haruchinsan 喜欢的演员,那么这部电影被称为是 Haruchinsan 最爱的电影。

由于 Haruchinsan 最近沉迷于贝斯弹奏中,所以请你帮助她确定每部电影的属于以下哪种情况:

是否一定会是她最爱的电影。
是否一定不会是她最爱的电影。
可能是最爱的电影,也可能不是。
输入格式
第一行是两个整数 m 和 k ,表示演员的数量和 Haruchinsan 喜欢的演员数量。
第二行包含 k 个不同的整数 ai ,表示喜欢的演员编号。
第三行是一个整数 n ,表示记录的电影数量。
接下来共 n 块,第 i 块描述第i部电影的相关信息:
第一行一个字符串 si,si 仅包含小写英文字母,长度为 1 到 10 ,表示电影名。
第二行一个非负整数 di(1≤di≤m),表示这部电影的演员数量。
第三行有 di 个整数 bi,j(0≤bi,j≤m),如果 bi,j 等于 0 ,那么第 j 个演员的编号看不清了,否则表示第 j 个演员的编号。保证这一行同一个编号不会出现两次。
保证所有的电影名都不同。数字之间以一个空格隔开。

输出格式
输出 n 行表示电影的信息。第 i 行输出:
0:如果第 i 部电影一定是最爱的电影。
1:如果第 i 部电影一定不是最爱的电影。
2:如果第 i 部电影可能是最爱的电影。

样例数据 1
输入  [复制]

5 3
1 3 5
4
jumanji
3
0 0 0
theeagle
5
1 2 3 4 0
matrix
3
2 4 0
sourcecode
2
2 4
输出

2
0
1
1
备注
【数据范围】
对于 100% 的数据:1≤m,n≤100, 1≤k≤m。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<queue>
using namespace std;

int m,k,d,n,a[105],minn[105],maxx[105],minnx,maxxx;
char s[10];

int main()
{
    freopen("cinema5.in","r",stdin);
    //freopen("cinema.out","w",stdout);
    memset(a,0,sizeof(a));

    minnx = 0, maxxx = 0;

    cin >> m >> k;

    a[0] = -1;

    for(int i=1;i<=k;i++)
    {
        int a1;
        cin >> a1;
        a[a1] = 1;
    }

    cin >> n; 
    int nx = n;
    while(nx--)
    {
        int d,b,jud=0;
        scanf("%s",s);
        cin >> d;
        int mx = m-k;
        int last = k;
        int dx = d;
        while(dx--)
        {
            cin >> b;
            if(a[b]==0) mx--;
            else if(a[b]+1==0)  jud++;
            else 
            {
                minn[n-nx]++;
                last--;
            }
        }

        maxx[n-nx] = minn[n-nx] + min(jud,last);

        if(mx<=jud) 
            minn[n-nx] += jud-mx;   

        minnx = max(minnx,minn[n-nx]);      
    }

    if(n==1)    
    {
        cout << 0 << endl;
        return 0;
    }

    int judd=0;
    for(int i=1;i<=n;i++)
    {
        maxxx = 0;
        for(int j=1;j<=n;j++)
            if(i!=j)
                maxxx=max(maxxx,maxx[j]);///?????????????????????

        if(minn[i]>=maxxx)
            cout << 0 << endl;      
        else if(maxx[i]<minnx)
            cout << 1 << endl;
        else
            cout << 2 << endl;
    } 
    return 0;
}

这里有一个相当不了解的地方,还请大神求解:(以下为我之前只过了80%的代码后半段)

    maxx[n-nx] = minn[n-nx] + min(jud,last);

        if(mx<=jud) 
            minn[n-nx] += jud-mx;   

        minnx = max(minnx,minn[n-nx]);
        maxxx = max(maxxx,maxx[n-nx]);
    }

    if(n==1)    
    {
        cout << 0 << endl;
        return 0;
    }

    int judd=0;
    for(int i=1;i<=n;i++)
    {

        if(minn[i]>=maxxx)
            cout << 0 << endl;      
        else if(maxx[i]<minnx)
            cout << 1 << endl;
        else
            cout << 2 << endl;
    } 
    return 0;
}

为什么maxxx的计算要在后面进行还要套一个for循环??


2.最大子数组

题目背景
SOURCE:NOIP2015-LC

题目描述
给出一个二维数组 a ,它有 n 行 m 列,并且 a[i][j] 只可能是整数 0 和 1 。它的一个子数组指的是某些连续的行和某些连续的列构成的一个二维数组。你现在可以任意地交换 a 的两行,并且可以交换任意多次。问能够得到的最大的全为 1 构成的子数组包含多少个 1 ?

输入格式
第一个两个正整数 n 和 m ,以一个空格隔开。
接下来 n 行,每行 m 个 0 和 1 ,数字中间没有空格。

输出格式
输出一个整数,表示答案,如果没有满足要求的数组,输出 0 。

样例数据 1
输入  [复制]

3 3
110
101
111
输出

4
备注
【样例解释】
交换第二行和第三行,我们得到这样的一个新数组:
110
111
101
新数组的前两行和前两列构成了一个大小为 2×2 的全由 1 构成的子数组。

【数据规模】
对于 10% 的数据:1≤n,m≤10。
对于 20% 的数据:1≤n≤10;1≤m≤5000。
对于 30% 的数据:1≤n≤20;1≤m≤50。
对于 40% 的数据:1≤n,m≤5000。

以下答案;我的暴力。。。直接炸

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
int n,m,maxn=0,f[5010][5010],ans[5010];
char s;
int main()
{
    //freopen("matrix.in","r",stdin);
    //freopen("matrix.out","w",stdout);
    scanf("%d\n",&n);
    scanf("%d\n",&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            s=getchar();
            if(s=='1')f[i][j]=f[i][j-1]+1;          
        }
        s=getchar();
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<=m;j++)ans[j]=0;
        for(int j=1;j<=n;j++)
            ans[f[j][i]]++;
        for(int j=m;j>=1;j--)
        {
            if(ans[j]*j>maxn)maxn=ans[j]*j;
            ans[j-1]=ans[j-1]+ans[j];
        }
    }
    cout<<maxn;
    return 0;
}

。。感觉自己得好好补补Dp了。。两次测试四道题都要用。。。


3.树

题目背景
SOURCE:NOIP2015-LC

题目描述
给出一颗树,你可以删除掉任意数量的边(可能不删边),问如何删边才能使得剩下的连通块的大小的积最大?

输入格式
第一行一个整数 n ,表示树中点的个数。
接下来 n-1 行,每行两个数 ai,bi(1≤ai≠bi≤n)表示一条边。

输出格式
输出一个整数,表示可能的最大结果。

样例数据 1
输入  [复制]

5
1 2
2 3
3 4
4 5
输出

6
备注
【样例解释】
删除 (2,3) 这条边,剩下两个大小为 2 和 3 的连通块,它们的积为 6 。

【数据规模】
对于 10% 的数据:1≤n≤20。
对于 20% 的数据:1≤n≤100。
对于 30% 的数据:1≤n≤500。
对于 40% 的数据:1≤n≤700。

我只想说,千万别被以为数据很小,事实证明这题竟然还要用Dp+高精度乘法+压位。。。暴力没过我心服口服(高精度运算学了就没用过。。)。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;

const int l = 55;
const int K = 10000;
const int N = 700+50;
const int E = 2000+50;

struct BigInt{
    int n,a[l+2];
    void clear(){
        memset(a,0,sizeof(a));
        n = 0;
    }
    void set(int x){
        clear();
        a[0] = x;
    }
    BigInt(int x = 0){
        set(x);
    }
    void operator +=(const BigInt &o){
        n = max(n,o.n);
        int tmp = 0;
        for(int i=0;i<=n;i++){
            tmp += o.a[i] + a[i];
            a[i] = tmp%K;
            tmp /= K;
        }
        while(tmp>0) a[++n] = tmp%K,tmp/=K;
    }
    void operator *=(int x){
        int tmp = 0;
        for(int i=0;i<=n;i++){
            tmp += a[i]*x;
            a[i] = tmp%K;
            tmp /= K;
        }
        while(tmp>0) a[++n] = tmp%K,tmp/=K;
    }
    BigInt operator *(int x){
        BigInt ret = *this;
        ret *=x;
        return ret;
    }
    void operator *=(const BigInt &o){
        static int c[l+2];
        memset(c,0,sizeof(int)*(n+o.n+2));
        for(int i=0;i<=n;i++)
            for(int j=0;j<=o.n;j++)
                c[i+j]+=a[i]*o.a[j];
        n+=o.n;
        int tmp = 0;
        for(int i=0;i<=n;i++){
            tmp+=c[i];
            a[i] = tmp%K;
            tmp/=K;
        }
        while(tmp>0) a[++n] = tmp%K,tmp/=K;
    }
    BigInt operator *(const BigInt &o){
        BigInt ret = *this;
        ret *=o;
        return ret;
    }
    bool operator <(const BigInt &o)const{
        if(n!=o.n)
            return n<o.n;
        for(int i=n;i>=0;i--)
            if(a[i]!=o.a[i])
                return a[i]<o.a[i];
        return false;
    }
    void show(){
        printf("%d",a[n]);
        for(int i=n-1;i>=0;i--)
            printf("%04d",a[i]);
        puts("");
    }
};
int n,root,cnt;
int first[N],to[E],next[E],size[N];
bool vis[N];
BigInt f[N][N];
int get_int()
{
    int ret = 0;
    char ks = getchar();
    for(;ks<'0'||ks>'9';ks=getchar());
    for(;ks<='9'&&ks>='0';ks=getchar())
        ret=ret*10+ks-'0';
    return ret;
}
void update(BigInt  &x,const BigInt &t)
{
    if(x<t)
        x = t;
}
void build(int x,int y)
{
    cnt++;
    next[cnt] = first[x];
    first[x] = cnt;
    to[cnt] = y;
}
void dfs_sec(int x)
{
    vis[x] = true;
    size[x] = 1;
    f[x][1].set(1);
    for(int i = first[x];i;i=next[i])
        if(!vis[to[i]]){
            dfs_sec(to[i]);
            for(int j = size[x];j>=0;j--)
                for(int jj=size[to[i]];jj>=0;jj--)
                    update(f[x][j+jj],f[x][j]*f[to[i]][jj]);
            size[x]+=size[to[i]];
        }
    for(int i=1;i<=size[x];i++)
        update(f[x][0],f[x][i]*i);
}
int main()
{
    n = get_int();
    for(int i=1;i<n;i++){
        int xx = get_int();
        int yy = get_int();
        build(xx,yy);
        build(yy,xx);
    }
    root = (1+n)>>1;
    dfs_sec(root);
    f[root][0].show();
    return 0;
}

头疼

总之,我还是先回去把DP搞定了再说orz

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值