Codeforces Round #216 (Div. 2)部分题解

题型:小贪心
题意:
主角有两种干净的餐具,1号餐具m个,2号k个
他每天用一个盘子吃一种套餐,而且用了不洗,而是等到没盘子用了才会洗一个,
他吃1号餐时必须用1号餐具,但是吃二号餐时可以用1号或2号餐具。
现在给一个序列,第i个数代表第i天他会吃x号餐,
问它至少洗多少次盘子

思路:
小模拟

代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int a[1000+10];

int main()
{
    int n,m,k;
    cin >> n >> m >> k;

    int cnt=0;
    for(int i = 0 ; i < n; i++)
    {
        int a;
        cin >> a;

        if(a == 1) m--;
        else
        {
            if(k >0)
                k--;
            else if(m > 0)
                m--;
            else
                k--;
        }

        if(m < 0)
            m=0,cnt++;
        if(k < 0)
            k=0,cnt++;
    }

    cout << cnt <<endl;

    return 0;
}


题型:数学
题意:
有n个数,总和sall,前k大的数和为sk,每个数不小于l,不大于r , 求这个序列

思路:
采取的策略是:
前k个数,从sk/k加起,一直加到sk加完,后n-k个数从(sall-sk)/(n-k)加起,直到sall加完
可以发现采取这样的策略的话,这题的不小于l和不大于r的条件完全可以无视,因为情况必定存在,所以这个条件必定成立(以前k大的数为例,若出现小于l的,说明补完sk之后必定还是存在小于l的数,矛盾,大于的类似)

注意n-k为0要特判,因为这个被别人黑了一次

代码:
特判比较挫,不想改了
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int main()
{
    int n,k,l,r,sall,sk;
    scanf("%d%d%d%d%d%d",&n,&k,&l,&r,&sall,&sk);

    if(n == k)
    {
        int ave = sall/n;
        sall -= ave*n;
        for(int i = 0 ; i < n ; i++)
            if(i)
                printf(" %d" , ave + (--sall >= 0));
                else
                    printf("%d" , ave + (--sall >= 0));
                puts("");
        return 0;
    }

    int ave = sk/k;
    int tmp = sall-sk;
    sk -= ave*k;
    int aave = tmp/(n-k);
    tmp -= aave*(n-k);

    for(int i = 0 ; i < n ; i++)
    {
        if(i < k)
            printf("%d " , ave + (--sk >= 0));
        else
        {
            if(i!=n-1)
                printf("%d " , aave + (--tmp>=0));
            else
                printf("%d\n" , aave + (--tmp >=0));
        }
    }

    return 0;
}


题型:图论,多叉树,dfs
题意:
给一颗多叉树(顶点数为n,边数为n-1的无向图),一行三个数表示相邻的点和权值
权值为1表示这条边不用修理,2表示要修理
现在从1节点开始通向各个点,如果到达一个点之前有需要修理的边,就把这个点归进一个集合
输出最小集合

思路:
如果1-i,i-n之间有需要修的路,压入的是n而不是i,最基本的贪心
一个点之后如果没有需要修理的路,并且这个点和上一个点的路恰好需要修理,那么这个点一定是要加入集合的

问题就是如何确定这样的点:
方法就是回溯,用一个res += dfs()记录接下来的边状态,dfs函数:如果后续没有节点或者没有需要修理的路,就返回0,
这样如果res为0的话,说明回溯上来的路径中没有需要修的边 , 若同时权值w == 2的话,这个点就被加入了

代码如下:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "vector"
#include "map"
#include "stack"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
const double eps = 1e-10;

struct edge
{
    int now;
    int next;
    int w;
}e[300000+100];

int n;

int head[100000+100];
int vis[100000+100];
int cnt=0;
vector<int> out;

void add(int x , int y , int v)
{
    e[cnt].now = y;
    e[cnt].next = head[x];
    e[cnt].w = v;
    head[x] = cnt++;
}

int dfs(int now , int  w)
{
    vis[now] = 1;
    int res=0;

    for(int i = head[now] ; i != -1 ; i = e[i].next)
    {
        int next = e[i].now;
        if(!vis[next])
            res += dfs(next , e[i].w);
    }

    if(!res && w == 2)
    {
        out.push_back(now);
        return 1;
    }

    return res;
}

int main()
{
    cin >> n;
    memset(head,-1,sizeof(head));

    for(int i = 1 ; i < n ; i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
        add(b,a,c);
    }

    dfs(1,1);

    cout << out.size() << endl;
    for(int i = 0 ; i < out.size() ; i++)
        if(i)
            cout << ' ' << out[i] ;
        else
            cout << out[i] ;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值