暑假第二十三测

题解:

第一题:

贪心

 

状压:我们发现110,100这样一列有两个1的一定不会组合,就算他们和后面凑成了0,那么其中一个必定可以和后面的构成0,所以我们不考虑他们的转移;

而0和1组合和0和0组合是等价的,因为我们不考虑一列两个1,所以i+ j ==> i&j, 最后看是否转移为0;

 

#include<bits/stdc++.h>
using namespace std;
const int M = (1 << 4) + 10; 
bool dp[2][M];
int ww[M][M], w[M][M], zw[M][M], a[5];

inline int get(int m){
    if(m == 1)return a[1];
    if(m == 2)return (a[1]<<1) + a[2];
    if(m == 3)return (a[1]<<2) + (a[2]<<1) + a[3];
    return (a[1]<<3) + (a[2]<<2) + (a[3]<<1) + a[4];
}
void init(){
    memset(ww, -1, sizeof(ww));
    memset(w, -1, sizeof(w));
    memset(zw, -1, sizeof(zw));
    for(int i = 0; i < (1 << 2); i++){
        for(int j = 0; j < (1 << 2); j++){
            bool fg = 0;
            for(int k = 0; k < 2; k++){ 
                if(((1<<k) & i) && ((1 << k) & j))fg = 1;    
            }if(!fg)ww[j][i] = i & j;
        }
            
        
    }
    for(int i = 0; i < (1 << 3); i++){
        for(int j = 0; j < (1 << 3); j++){
            bool fg = 0;
            for(int k = 0; k < 3; k++){ 
                if(((1<<k) & i) && ((1 << k) & j))fg = 1;    
            }if(!fg)zw[j][i] = i & j;
        }
            
        
    }
    
    for(int i = 0; i < (1 << 4); i++){
        for(int j = 0; j < (1 << 4); j++){
            bool fg = 0;
            for(int k = 0; k < 4; k++){ 
                if(((1<<k) & i) && ((1 << k) & j))fg = 1;    
            }if(!fg)w[j][i] = i & j;
        }
            
        
    }
}

int main(){
    freopen("prob.in","r",stdin);
    freopen("prob.out","w",stdout);
    int t, tt = 0;
    scanf("%d", &t);
    init();
    while(t--){
        int n, m, now = 0;
        bool fg = 0;
        scanf("%d%d", &n, &m);
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++)scanf("%d", &a[j]);
            if(fg)continue;
            int s = get(m);
            now ^= 1;
            dp[now][s] = 1;
            if(m == 1){
                if(!s)fg = 1;
            }
            else if(m == 4)
                for(int sc = 0; sc < (1 << 4); sc++){
                    if(dp[now^1][sc]){
                        dp[now][sc] = 1;
                        if(w[s][sc] != -1)dp[now][w[s][sc]] = 1;
                    }
                }
            else if(m == 2){
                for(int sc = 0; sc < (1 << 2); sc++){
                    if(dp[now^1][sc]){
                        dp[now][sc] = 1;
                        if(ww[s][sc] != -1)dp[now][ww[s][sc]] = 1;
                    }
                }
            }
            else {
                for(int sc = 0; sc < (1 << 3); sc++){
                    if(dp[now^1][sc]){
                        dp[now][sc] = 1;
                        if(zw[s][sc] != -1)dp[now][zw[s][sc]] = 1;
                    }
                }
            }
            if(dp[now][0])fg = 1;
            //if(fg)cout<<i<<"KKKKKKKK";
            
        }
        //printf("%d %d %d ",++tt, n, m);
        fg ? puts("YES") : puts("NO"); 
    }
}
View Code

 

第二题:贪心

#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 10;
#define ll long long
int n;
struct pp{ll s,a,b; int id;}st1[M], st2[M], p[M];
ll sum[2], tot[2], yp[2];
bool cmp(pp T, pp C){
    return T.a - T.b > C.a - C.b;
}
bool cmp2(pp T, pp C){
    return T.b - T. a > C.b - C.a;
}
int main()
{
    freopen("pizza.in","r",stdin);
    freopen("pizza.out","w",stdout);
       ll totm = 0, SS;
    scanf("%d%I64d", &n, &SS);
    for(int i = 1; i <= n; i++){
        scanf("%I64d%I64d%I64d", &p[i].s, &p[i].a, &p[i].b);
        p[i].id = i;
        if(p[i].a >= p[i].b)st1[++tot[1]] = p[i], sum[1] += p[i].s * p[i].a, yp[1] += p[i].s;
        else st2[++tot[2]] = p[i], sum[2] += p[i].s * p[i].b, yp[2] += p[i].s;
        totm += p[i].s;
    }
    sort(st1 + 1, st1 + 1 + tot[1], cmp);
    sort(st2 + 1, st2 + 1 + tot[2], cmp2);
    
    
    ll cas1, cas2;
    cas1 = cas2 = sum[2] + sum[1];
    ll ned = ceil(1.0*totm / SS);
    ll h1 = ceil(1.0*yp[1] / SS);
    ll h2 = yp[2] - (ned - h1) * SS;
    int pw = tot[2];
    while(h2 > 0){
        if(h2 < st2[pw].s) {
            cas1 -= (st2[pw].b - st2[pw].a) * h2;
            h2 = 0;
        }
        else {
            cas1 -= (st2[pw].b - st2[pw].a) * st2[pw].s;
            h2 -= st2[pw].s;
        }
        pw--;
    }
    h2 = yp[1] - (h1 - 1) * SS;
    pw = tot[1];
    while(h2 > 0){
        if(h2 < st1[pw].s) {
            cas2 -= (st1[pw].a - st1[pw].b) * h2;
            h2 = 0;
        }
        else {
            cas2 -= (st1[pw].a - st1[pw].b) * st1[pw].s;
            h2 -= st1[pw].s;
        }
        pw--;
    }    
    printf("%I64d\n", max(cas1, cas2));
    
}
View Code

 

第三题:线段树优化dp

对于c(k,i)的修改,我们只需要记录一个lst的数组,那么他就制度lst + 1 到i有贡献;

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

const int M = 20000 + 5, inf = 1e9;
int n, m, pre[M], a[M], pos[M], dp[2][M];
struct Node{
    Node *ls, *rs;
    int v, tag;
    void up(){
        v = max(ls->v , rs->v);
    }
    
    void down(){
        if(tag){
            ls->tag += tag; rs->tag += tag;
            ls->v += tag; rs->v += tag;
            tag = 0;
        }
    }
}pool[M << 2], *root, *tail = pool;


#define Ls nd->ls, lf, mid
#define Rs nd->rs, mid + 1, rg
int query(int L, int R, Node *nd = root, int lf = 1, int rg = n){    
    if(L <= lf && rg <= R)return nd->v;
    nd->down();
    int mid = (lf + rg) >> 1;
    int v = -inf;    
    if(L <= mid)v = query(L, R, Ls);
    if(R > mid)v = max(v, query(L, R, Rs));
    return v;
}
void modify(int L, int R, int val, Node *nd = root, int lf = 1, int rg = n){
    if(L <= lf && rg <= R){
        nd->v += val; nd->tag += val;
        return ;
    } 
    nd->down();
    int mid = (lf + rg) >> 1;
    if(L <= mid)modify(L, R, val, Ls);
    if(R > mid)modify(L, R, val, Rs);
    nd->up();
}

Node *build(int vin, int lf = 1, int rg = n){
    Node *nd = ++tail;
    if(lf == rg)nd->v = dp[vin][lf], nd->tag = 0;
    else {
        int mid = (lf + rg) >> 1;
        nd->ls = build(vin, lf, mid);
        nd->rs = build(vin, mid + 1, rg);
        nd->tag = 0;
        nd->up();
    }
    return nd;
}

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]);
    for(int i = 1; i <= n; i++){
        pre[i] = pos[a[i]];
        pos[a[i]] = i;
    }
    int now = 0, lst = 1;
    for(int i = 1; i <= n; i++)dp[lst][i] = dp[lst][i - 1] + (pre[i] == 0);
    for(int k = 2; k <= m; k++){
        tail = pool;
        root = build(lst);
        for(int i = 1; i <= n; i++){
            if(i < k)dp[now][i] = -inf;
            else {
                modify(max(pre[i], 1), i - 1, 1);
                dp[now][i] = query(1, i - 1);
            }
        }
        swap(now, lst);
    }
    printf("%d\n", dp[lst][n]);
}
View Code

 

今天前两道题都是贪心,我却一直在想dp, 贪心还是太烂了, 必须要加强重视了;

posted @ 2018-08-23 21:48  Ed_Sheeran  阅读(177)  评论(0编辑  收藏  举报