18.8.23 考试总结

今天的考试我是真的...emmmm

考试的时候我乱搞 然后狗了三十分...

一些大佬用状压dp弄得 然后还有另外一些大佬是贪心枚举所有情况 正解是非常简短的

这道题可以总结出一个规律 当m ≤ 5的时候 可以推出如果两行的数&起来是 0 那么就一定合法

证明: 因为m ≤ 5    又因为做过的题目小于等于没做过的 所以说0的个数一定大于等于1的个数

那么0最多的一行肯定至少有ceil(m / 2)个0 否则0就不够多 

那么该行就有至多两个1  如果剩下的方案中没有一种方案在那两个1对应的两列全为0

那么该列0的个数就永远不可能大于等于1 就不合法

因为如果只有一列为0 另一列是1 那么那两列1的个数永远大于0的个数 就不可能合法

所以只需要找到两个&起来为0的就可以了 只有一个1的同理 

代码

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

const int N = 1000;
int T,n,vis[N],x,m;

inline int read( ) {
    
    int ans = 0,t = 1;
    char x; x = getchar( );
    while(x < '0' || x > '9') {
        if(x == '-') t = -1;
        x = getchar( );
    }
    while(x >= '0' && x <= '9') {
        ans = ans * 10 + x - '0';
        x = getchar( );
    }
    return ans * t;
}

int main( ) {
    
    freopen("prob.in","r",stdin);
    freopen("prob.out","w",stdout);
    T = read( );
    while(T --) {
        memset(vis,0,sizeof(vis));
        n = read( ); m = read( );
        for(int i = 1;i <= n;i ++) {
            int v = 0;
            for(int j = 1;j <= m;j ++) {
                x = read( );
                v <<= 1; v |= x;
            } 
            vis[v] = true;
        }
        for(int i = 0;i < (1 << m);i ++)
          for(int j = 0;j < (1 << m);j ++)
            if(!(i & j) && vis[i] && vis[j]) {
                printf("YES
");
                goto Next;
            }
        printf("NO
");
        Next:;
    }
}

这道题就是一道贪心...然后细节有点多

我们考虑把所有客人分为两组 a ≥ b的分为第一组 a < b 的分为第二组

然后再对两组分别排序 如果是第一组 则按照a - b的大小从大到小排序

另外一组则是b - a的大小从大到小排序

对于第一组而言 a - b的大小越大 那么也就是说如果让这个客人去吃第二组的饼饼 他亏得高兴度就会更多

所以我们想要让亏掉的高兴度最少 那么就优先满足a - b大的 第二组同理

那么现在问题就是如何分配饼饼 因为总饼数是一定的

所以我可能是第一组全部吃到喜欢的饼饼第二组的饼饼不够吃  然后第二组的人最后那几个就去吃第一组的饼饼

要不就是第一组的不够吃 最后几个人去吃第二组的 所以就乱搞搞判断就可以了

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
ll sum = 0,ans,sum1,sum2,S;
int n,m,num1 = 0,num2 = 0;
struct gues {
    
    ll s;
    int a,b;
}g1[N],g2[N];

inline int read( ) {
    
    int ans = 0,t = 1;
    char x; x = getchar( );
    while(x < '0' || x > '9') {
        if(x == '-') t = -1;
        x = getchar( );
    }
    while(x >= '0' && x <= '9') {
        ans = ans * 10 + x - '0';
        x = getchar( );
    }
    return ans * t;
}

bool cmp1(const gues & q,const gues & p) {
    
    return q.a - q.b > p.a - p.b;
}

bool cmp2(const gues & q,const gues & p) {
    
    return q.b - q.a > p.b - p.a;
}

void solve(ll s1,ll s2) {
    
    ll aa = s1 * S,bb = s2 * S;
    ll cmp = 0,rem = g1[1].s;
    int i = 1; 
    for(i = 1;i <= num1 && aa > 0;i ++) {
        ll del = min(g1[i].s,aa);
        cmp += (1LL * g1[i].a * del);
        aa -= del;
        rem = g1[i].s - del;
    }

    if(i > 1) i --;
    if(s1 * S < sum1) {
        cmp += rem * 1LL * g1[i].b; i ++;
        bb -= rem;
        for(;i <= num1;i ++) {
            cmp += (1LL * g1[i].b * g1[i].s);
            bb -= g1[i].s;
        }
    }
    int j = 1;
    rem = g2[1].s;
    for(j = 1;j <= num2 && bb > 0;j ++) {
        ll del = min(g2[j].s,bb);
        cmp += (1LL * g2[j].b * del);
        bb -= del;
        rem = g2[j].s - del;
    }
    if(j > 1) j --;
    if(s2 * S < sum2) {
        cmp += rem * 1LL * g2[j].a; j ++;
        aa -= rem;
        for(;j <= num2;j ++) {
            cmp += (1LL * g2[j].a * g2[j].s);
            aa -= g2[j].s;
        }
    }
    ans = max(ans,cmp);
}

int main( ) {
    
    freopen("pizza.in","r",stdin);
    freopen("pizza.out","w",stdout);
    scanf("%d%I64d",& n,& S);
    for(int i = 1;i <= n;i ++) {
        int s,a,b;
        s = read( ); a = read( ); b = read( );
        gues w; w.s = (ll)s; w.a = a,w.b = b;
        if(a >= b) {
            g1[++ num1] = w;
            sum1 += (ll)s;
        }
        else {
            g2[++ num2] = w;    
            sum2 += (ll)s;
        }
        sum += (ll)s;
    }
    
    m = (sum % S == 0) ? 1LL * sum / S : 1LL * sum / S + 1;
    sort(g1 + 1,g1 + num1 + 1,cmp1);
    sort(g2 + 1,g2 + num2 + 1,cmp2);
    solve(sum1 / S,m - sum1 / S);
    if(m - sum1 / S - 1 >= 0)
       solve(sum1 / S + 1,m - sum1 / S - 1);
    if(sum1 / S  - 1 >= 0)
       solve(sum1 / S  - 1,m - sum1 / S + 1);
    printf("%I64d",ans);
}

这道题可以说是真够鹅星了...呕

是一道线段树优化dp的问题 dp方程是显而易见的 

dp[ i ][ j ]表示我选择i个冰淇淋 用了j个桶桶所能获得的最大收益

那么转移是和柠檬那道题 诗人小G很像的 dp[ i ][ j ] = dp[ k ][ j - 1 ] + c[ k + 1 ][ i ]

c[ k + 1 ][ i ]表示k + 1到 i 这一段的贡献 也就是有是多少种不同的冰淇淋

朴素的转移是枚举 i j k 然后暴力跑出来 k + 1 到 i 的贡献 但是这复杂度就是O(m * n2)

非常不优秀 然后我们可以发现dp只和上一层j - 1有关 就可以滚一滚

然后发现dp[ i ][ j ]是在1 ~ i - 1里面去一个max 所以考虑用线段树维护区间最大值

但是由于在dp的时候随着i的变化 c也是随着变化的 所以还要考虑怎么维护c

对于位置i 他比i - 1多了a[ i ]这一种颜色 但是a[ i ]只会在和他一样的冰淇淋上一次出现的位置 + 1 ~ i产生贡献

所以每次就只用在他上一次出现的位置  + 1 ~ i加上1就可以了 然后查询区间max

(这份代码线段树维护的是dp[ k ][ j ] + c[ k + 1 ][ i ]) 所以每次修改是从prev[i] 开始改到i - 1 我当时理解了好久...

代码

#include <bits/stdc++.h>
#define oo 1e7
using namespace std;

const int N = 2 * 1e5 + 5;
int n,m,tag[4 * N],f[4 * N],dp[N][2],a[N];
int prev[N],las[N],now;

void push_down(int o) {
    
    if(tag[o]) {
        f[2 * o] += tag[o];
        f[2 * o + 1] += tag[o];
        tag[2 * o] += tag[o];
        tag[2 * o + 1] += tag[o];
        tag[o] = 0;
        return ;
    }
}

void update(int o) {
    
    f[o] = max(f[2 * o],f[2 * o + 1]);
}

void modify(int o,int l,int r,int L,int R,int del) {
    
    if(l >= L && r <= R) {
        f[o] += del;
        tag[o] += del;
        return ;
    }
    push_down(o);
    int mid = (l + r) >> 1;
    if(L <= mid) modify(2 * o,l,mid,L,R,del);
    if(mid < R)  modify(2 * o + 1,mid + 1,r,L,R,del);
    update(o);
}

int query(int o,int l,int r,int L,int R) {
    
    if(l >= L && r <= R) {
        return f[o];
    }
    push_down(o);
    int mid = (l + r) >> 1;
    int ans = 0;
    if(L <= mid) ans = max(ans,query(2 * o,l,mid,L,R));
    if(mid < R)  ans = max(ans,query(2 * o + 1,mid + 1,r,L,R));
    return ans;
}

void build(int o,int l,int r) {
    
    tag[o] = 0;
    if(l == r) {
        f[o] = dp[l][now ^ 1];
        return ;
    }
    int mid = (l + r) >> 1;
    build(2 * o,l,mid);
    build(2 * o + 1,mid + 1,r);
    update(o);
}

int main( ) {
    
    freopen("scream.in","r",stdin);
    freopen("scream.out","w",stdout);
    scanf("%d%d",& n,& m);
    for(int i = 1;i <= n;i ++) {
        scanf("%d",& a[i]);
        prev[i] = las[a[i]];
        las[a[i]] = i;
    }
    now = 0;
    for(int i = 1;i <= n;i ++) dp[i][now] = dp[i - 1][now] + (prev[i] == 0);
    for(int j = 2;j <= m;j ++) {
        now ^= 1;
        build(1,1,n);
        for(int i = 1;i <= n;i ++) {
            if(i < j) dp[i][now] = -oo;
            else {
                int pos = max(1,prev[i]);
                modify(1,1,n,pos,i - 1,1);
                dp[i][now] = query(1,1,n,1,i - 1);
            }
        }
    }
    printf("%d",dp[n][now]);
}
原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9525110.html