18.9.7 考试总结

啊今天是生日之后的第三天呢 然后我就大凶了.. 略微难受

啊这道题正解是倒着搞得 ()然而蒟蒻我并不会... 于是就学习wans大佬的程序写的二分 + 正着搞得$Spfa$

$Spfa$最坏是 $nm$ 显然这道题是可以直接跑过的

所以对于起点二分一个最大货物量 然后Spfa跑每个点的最大值就可以了 按照题目意思来稿还是很清楚的

代码

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

const int N = 2000 + 5;
int m,n,dis[N],head[N],nex[2 * N],tov[2 * N],p;
int tot,s,t,cas;
bool vis[N];
queue<int>Q;

int deal(char c) {

    if(c >= ‘a‘ && c <= ‘z‘) return c - ‘a‘;
    else return c - ‘A‘ + 26;
}

void add(int u, cint v) {

    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    head[u] = tot;
}

void add_Edge( ) {

    for(int i = 1;i <= n;i ++) {
        char a[2],b[2];
        scanf("%s%s",a,b);
        int u = deal(a[0]),v = deal(b[0]);
        add(u,v); add(v,u);
    }
}

bool Spfa(int sum) {

    memset(dis,128,sizeof(dis));
    memset(vis,0,sizeof(vis));
    vis[s] = true;
    Q.push(s);
    dis[s] = sum;
    while(! Q.empty( )) {
        int u = Q.front( ); Q.pop( );
        vis[u] = false;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(v <= 25) {
                if(dis[v] < dis[u] - 1) {
                    dis[v] = dis[u] - 1;
                    if(! vis[v]) {
                        vis[v] = true;
                        Q.push(v);
                    }
                }
            }
            else {
                if(dis[v] < dis[u] - (dis[u] + 19) / 20) {
                    dis[v] = dis[u] - (dis[u] + 19) / 20;
                    if(! vis[v]) {
                        vis[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
    }
    return dis[t] >= p;
}

void solve( ) {

    char a[2],b[2];
    scanf("%d%s%s",& p,a,b);
    s = deal(a[0]),t = deal(b[0]);
    int l = p,r = oo,ans = oo;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(Spfa(mid)) ans = mid,r = mid - 1;
        else l = mid + 1;
    }
    printf("Case %d: %d\n",++ cas,ans);
}

int main( ) {

    freopen("toll.in","r",stdin);
    freopen("toll.out","w",stdout);
    while(scanf("%d",& n) != EOF) {
        if(n == -1) break;
        memset(head,0,sizeof(head));
        tot = 0;
        add_Edge( );
        solve( );
    }
}

emmmmmmm这道题我一来先把图搞出来 .... 搞出来.... 然后就不会了

谁来告诉我无向图最大点独立集咋跑...???? 然后意识到这是错误的

wans$SD$真的tqltql 我要膜dalao orzorzorz

可以发现 因为这是冒泡排序 所以每出现一个逆序对都要进行连边

也就是说如果他是单调递增的 那么就可以保证他们之间两两没有边 也就是说这是一个独立集

那么也就是我如果我们找到一个最长上升子序列 那么这就是新图里的最大点独立集

然后就$nlogn$跑就可以了 我用的线段树弄的  至于求这个的方法就不说了 网上到处都是

代码

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

const int N = 1e5 + 5;
int n,a[N],f[4 * N];

int query(int o,int l,int r,int L,int R) {

    if(l >= L && r <= R) {
        return f[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 update(int o) {

    f[o] = max(f[2 * o],f[2 * o + 1]);
}

void modify(int o,int l,int r,int pos,int val) {

    if(l == r) {
        f[o] = max(f[o],val);
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) modify(2 * o,l,mid,pos,val);
    else modify(2 * o + 1,mid + 1,r,pos,val);
    update(o);
}

void solve( ) {

    int ans = 0;
    for(int i = 1;i <= n;i ++) {
        int num = query(1,1,n,1,a[i]);
        modify(1,1,n,a[i],num + 1);
        ans = max(ans,num + 1);
    }
    printf("%d",ans);
}

int main( ) {

    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    scanf("%d",& n);
    for(int i = 1;i <= n;i ++) scanf("%d",& a[i]);
    solve( );
}

这道题一看数据范围就是状压dp啊 那么就愉快的乱写了... 我那个乱七八糟的程序竟然都还有60分...尴尬

总之垃圾程序就不说了 直接说正解吧  用 $01$ 状态表示哪些门选择了 哪些门没选择 而这个状态对应的钥匙数量是一定的

那么现在在钥匙一样多的情况下 我们要尽量使白色的钥匙更多 这是一个贪心的策略啊 使用多余的白色钥匙去补齐不够的钥匙

如果白钥匙一样多怎么办呢 比如我现在这个状态我可以有 $3,2,0$ 或者 $2,3,0$ 的钥匙 那么对应的能开的门就会出现问题

第一种就开不了 $2,3$ 第二种就开不了$3,2$ 但是我们并不知道开哪种门会更优  如果只记录白钥匙的情况

我们就只能傻乎乎的存下第一种达到$maxwhite$的情况 而对应的我们并不知道我们能够更新到哪种状态才是更优秀的

怎么办呢 就再开一维就好了 再存一个红钥匙的个数 就不会出现情况互相冲突的问题了

那么就很朴素的转移就好了 然后在转移的时候判断一下答案的最大值就可以了

代码

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

const int N = 30;
int n,red[N],gre[N],rk[N],wk[N],gk[N],k1,k2,k3,aans,ss;
struct data {

    int r,w,g;
    int ans;
}dp[(1 << 15)][151];

void Scanf( ) {

    scanf("%d",& n);
    for(int i = 1;i <= n;i ++) scanf("%d",& red[i]);
    for(int i = 1;i <= n;i ++) scanf("%d",& gre[i]);
    for(int i = 1;i <= n;i ++) scanf("%d",& rk[i]);
    for(int i = 1;i <= n;i ++) scanf("%d",& gk[i]);
    for(int i = 1;i <= n;i ++) scanf("%d",& wk[i]);
    scanf("%d%d%d",& k1,& k2,& k3);
}

int check(int pos,int nr,int ng,int nw) {

    if(red[pos] + gre[pos] > nr + ng + nw) return -1;
    if(nr >= red[pos]) {
        nr -= red[pos];
    }
    else {
        int needw = red[pos] - nr;
        nw -= needw;
        if(nw < 0) return -1;
        nr = 0;
    }
    if(ng >= gre[pos]) {
        ng -= gre[pos];
    }
    else {
        int needw = gre[pos] - ng;
        nw -= needw;
        if(nw < 0) return -1;
        ng = 0;
    }
    ss = nw;
    return nw + nr + ng;
}

void modify(int sta,int s,int num,int nr,int ng,int nw,int pos) {

    if(check(pos,nr,ng,nw) >= 0) {
        int cmp = check(pos,nr,ng,nw);
        cmp += rk[pos],cmp += gk[pos],cmp += wk[pos];
        ss += wk[pos];
        aans = max(aans,cmp);
        if((ss >= dp[sta][num].w) && (dp[s][num].w != -1)) {
            dp[sta][max(nr - red[pos],0) + rk[pos]].r = max(nr - red[pos],0) + rk[pos];
            dp[sta][max(nr - red[pos],0) + rk[pos]].g = max(ng - gre[pos],0) + gk[pos];
            dp[sta][max(nr - red[pos],0) + rk[pos]].w = ss;
        }
    }
}

void solve( ) {

    for(int sta = 0;sta < (1 << 14) - 1;sta ++)
      for(int i = 0;i <= 150;i ++) dp[sta][i].w = -1;
    dp[0][k1].r = k1, dp[0][k1].g = k2, dp[0][k1].w = k3;
    aans = k1 + k2 + k3;
    dp[0][k1].ans = aans;
    for(int sta = 0;sta < (1 << 14) - 1;sta ++) {
        int s = sta;
        for(int num = 0;num <= 150;num ++)
        {
            for(int j = 0;j < n;j ++) {
                if(!(sta & (1 << j))) {
                    modify(sta | (1 << j),sta,num,dp[sta][num].r,dp[sta][num].g,dp[sta][num].w,j + 1);
                }
            }
        }
    }
    printf("%d",aans);
}

int main( ) {

    freopen("room.in","r",stdin);
    freopen("room.out","w",stdout);
    Scanf( );
    solve( );
}

为什么总是在我最狼狈的时候出现呢。

原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9607212.html

时间: 2024-10-31 09:54:16

18.9.7 考试总结的相关文章

18.1.1考试

今天考的题目都是DP,本来以为会有什么图论...根据今天题目比较水的原因,我直接放解题报告,大家应该可以看得懂.. T1 Source #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> using namespace std; const int maxn=10000+10,maxm=10010; #define file(a) freopen(a".i

18.8.13 考试总结

1.1 问题描述请构造一颗n 个节点的树,使得其价值最大.f(d) 表示树上,度数为d 的一个点能够获取的价值.这棵树的价值为Σni=1 f(di)di 表示第i 个点的度数1.2 输入第一行一个整数T,接下来T 组数据,每组数据输入两行.第一行输入整数n.第二行输入n ?? 1 个整数f(i) 代表f(1) f(n ?? 1).1.3 输出对于每组数据输出一行,为能够构造的树的最大价值. 一开始我以为是一道树形dp... 考完了才知道原来这个是一道背包问题 n个节点 总共有2n - 2个度数

18.8.26 考试总结

我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了20分.. 这道题是一道找规律的题 看完题很显然能够发现我们可以将相同颜色的连通块缩点 因为同一个联通块的可以一次操作全部变成另外一种颜色 所以就缩点就好了.. 对于缩点后的一条链 每次我们可以将一个点变色 那么和他相邻的点就和他颜色一样 然后就再次缩点 所以每次链的长度都可以 -2 所以说最后缩点的次数就是  点的个数 / 2  (下取整) 但是这是对于一条链 那么对于一棵树而言呢? 其实是一样的 因为每次搞这个操作 我们都可

18.9.22 考试总结

这道题一看就是可持久化并查集 然后我就愉快的yy了一波 还是错掉了qwqwqwqwq 方法是对的 就是我每次在树上查询$fa$的时候我还压缩了路径 导致这玩意空间炸掉了 所以要保证时间复杂度 就启发式合并 也就是$size$小的往$size$大的搞 这样子就保证每次合并的时候连通块元素个数每次至少乘以$2$ 也就是保证了层数是$log$级的 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n

18.8.17 考试总结

高斯消元[问题描述]everlasting 觉得太无聊了,于是决定自己玩游戏! 他拿出了n 个小圆,第i 个的颜色是ai.接着他将这n 个小圆复制m 次并依次连接起来. 之后他发现其中有很多颜色相同的小圆,于是他决定:每有k 个连续颜色相同的小圆就将他们消去, 并将剩下的依次连接.(注意只会消除k个,即使有超过k 个)他将每次从头开始不断进行这个操作直到无法操作为止. 他想知道最后能剩下多少个小圆? [输入格式] 从文件guass.in 中输入数据. 第一行三个正整数n,m,k,表示开始小圆的个

18.8.19 考试总结

Gift[问题描述]人生赢家老王在网上认识了一个妹纸,然后妹纸的生日到了,为了表示自己的心意,他决定送她礼物. 可是她喜爱的东西特别多,然而他的钱数有限,因此他想知道当他花一定钱数后剩余钱数无法再购买任何一件剩余物品 (每种物品他最多买一个)时有多少种方案,两种方案不同,当且仅当两种方案中至少有一件品不 同,可是由于他忙着准备泡下一个妹纸(chi),因此麻烦聪明的你帮帮忙. [输入格式] 输入第一行 n 和m,n 表示妹纸喜欢的礼物数目,m 表示现有的钱数,第二行n 个数,表示n 个物品的价格.

18.8.31 考试总结

这道题就是打表找规律 然而我打表并没有找到规律... yyyuuudalao就找到了tqltql%%% 打表发现 $ A $ 数列是这样子1 1 ,2 ,2 ,6 1 1 2 2 3 4 4 4 5 6 6 7 8 8 8 8 9 10 10 11 12 12 12 13 14 14 15 16 16 16 16 16... $ t[i] $ 表示 $ i $ 这个数出现次数 然后发现 $t[i]$ 为 $i$ 所含的因子 $2$ 个数 $+ 1$ ($1$除外) 也就是说每个数如果多一个 $2

18.9.8 考试总结

这道题就是树上贪心就好了 从下往上搞 要是遇到没有被选过得点就把他的父亲标记就好了 然后就 $bfs$ 把节点都存到一个栈里面 然后弹出的肯定是最深的点 就慢慢往上搞就可以了 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int stk[N],tot,top,fa[N],ans = 0; int n,head[N],nex[2 * N],tov[2 * N]; bool vis[N],arr[N]

18.10.4 考试总结

这道题就是一道肥!肠!裸!的!轮廓线dp 然后因为细节太多了还因为有一个sbsbsb编译错误 就是不准我函数名字取count...我恨 我永远讨厌轮廓线dp 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 5; int dp[2][N],n,m,len[200],cnt[N],ans; char s[200][40]; void Init( ) { scanf("%d%d",&