日常补题

CFR 363
给定一个序列:
ai equals 0, if on the i-th day of vacations the gym is closed and the contest is not carried out;
ai equals 1, if on the i-th day of vacations the gym is closed, but the contest is carried out;
ai equals 2, if on the i-th day of vacations the gym is open and the contest is not carried out;
ai equals 3, if on the i-th day of vacations the gym is open and the contest is carried out.
还有个要求:他不会连续两(>=2)天都做相同的运动: (=_=反正就是做活动要间隔像1,2,1,2,1,2)
问最少能休息几天。
dp[i][j]表示第 i 天做 j 类型的活动所能休息的最少时间。
如果x为1,则说明当天能休息或者去GYM,所以更新dp[ i ][1]和dp[ i ][0],同时当天不能做2类型的运动,所以dp[i][2]=INF;其他类型同理,递推下去就好了

/*cf 698A*/
#include<stdio.h>
#include<algorithm>
using namespace std;
int D[1005][3];
int main()
{
    //freopen("in.txt","r",stdin);
    int x,N;
    scanf("%d", &N);
    for (int i = 1; i <= N; i++)
    {
        scanf("%d",&x);
        if (x==3)
        {
            D[i][0] = min(min(D[i - 1][0], D[i - 1][1]),D[i-1][2])+1;
            D[i][1] = min(D[i - 1][0], D[i - 1][2]);
            D[i][2] = min(D[i - 1][0], D[i - 1][1]);
        }
        if (x==2)
        {
            D[i][0] = min(min(D[i - 1][0], D[i - 1][1]), D[i - 1][2]) + 1;
            D[i][2] = min(D[i - 1][0], D[i - 1][1]);
            D[i][1] = 100000000;
        }
        if (x==1)
        {
            D[i][0] = min(min(D[i - 1][0], D[i - 1][1]), D[i - 1][2]) + 1;
            D[i][2] = 100000000;
            D[i][1] = min(D[i - 1][0], D[i - 1][2]);
        }
        if (x==0)
        {
            D[i][0] = min(min(D[i - 1][0], D[i - 1][1]), D[i - 1][2]) + 1;
            D[i][1] = 100000000;
            D[i][2] = 100000000;
        }
    }
    printf("%d\n", min(min(D[N][0], D[N][1]), D[N][2]));


    return 0;

}

CF 698B
给一个数组a,
ai是 i 的父亲。
让你修改最少的元素pi让这数组变成一棵合法的树…

首先,要找出这个数组中有多少个环和多少个根节点( ai== i ) 的.
然后将每个环中的一个点当作这个环的根节点。
然后使用并查集解法是:
先把所有根节点都找出来,并将总的根节点num初始为其中的一个根节点。
再把所有的环都拆开,找出环中的一个点作为根节点a[i]==i。
然后把所有根结点都连向num。
注意: 优先把总根节点num初始化成a[i]==i,而不是拆开的环,因为要找的是操作数最少,找环的话操作次数会+1

这方法也是从CF一个看到的代码那里学习的…
我本来想把这棵树分成几个联通量…再把所有联通量都找出根节点,一 一连起来..
但是不知道怎么把环拆开…

/*并查集*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200050;
int a[maxn],f[maxn],ans[maxn];
int Find(int x)
{
    return f[x] == x?x:f[x] = Find(f[x]);
}
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    int n;
    scanf("%d",&n);
    for(int i = 1; i<=n; i++) f[i] = i;
    int cnt=0,num=-1;;
    for(int i = 1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        int x=Find(i),y=Find(a[i]);
        if(a[i]==i) {num=i,ans[++cnt] = i;continue;}//这是一个根节点
        else if(x==y)//ai!=i,而i,a[i]在一个联通块里,此时又要将i连向a[i],所以肯定会出现环。
        {
            a[i]=i;//将所有环都拆开,并以这个点为根节点。
            cnt++;
        }
        f[x]=y;
    }
    if(num==-1)//如果没有找到根节点,再从拆开的环里面找出一个结点当根节点。
    {
        for(int i=1;i<=n;i++)
            if(a[i]==i)
            {
                num=i;
                cnt++;//只有环的话,改变的元素会多一个。
                break;
            }
    }
    printf("%d\n",--cnt);
    for(int i=1;i<=n;i++)
    {
        if(i==a[i]) a[i]=num;//将所有根节点都连向一个根节点num
        printf("%d%c",a[i],i==n?'\n':' ');
    }
}

BFS解法…其实和并查集差不多..
只是我把每个联通量的根节点都存了起来,再把所有根节点都连向root…
就是BFS判环和并查集判环不同。

/*BFS*/
#include <bits/stdc++.h>
using namespace std;
int f[200006];
int circle[200006],cnt,fa[200006];
bool vis[200006];
vector<int >E[200006];
int num=-1,flag=0;
int bfs(int x)
{
    queue<int>Q;
    Q.push(x);
    int t=0;
    bool cir=0;
    while(!Q.empty())
    {
        x=Q.front();
        if(f[x]==x) t=x;
        Q.pop();
        for(int i=0;i<E[x].size();i++)
        {
            if(!vis[E[x][i]])
            {
                Q.push(E[x][i]);
                fa[E[x][i]]=x;
                vis[E[x][i]]=1;
            }
            else if(fa[x]!=E[x][i]&&!cir)
            {
                if(num==-1) num=x;
                t=x;
                cir=1;//标记,免得环里的元素重复出现...
            }
        }
    }
    return t;//一个联通量里只会有一个根节点或者一个环
}
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i]);
        if(f[i]==i) flag=1,num=i;
        E[i].push_back(f[i]);
        E[f[i]].push_back(i);
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;//忘了这句一直WA...MDZZ
            int k=bfs(i);
            //cout<<k<<endl;
            circle[++cnt]=k;
        }
    }
    printf("%d\n",cnt-flag);
    for(int i=1;i<=cnt;i++)
    {
        f[circle[i]]=num;
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d%c",f[i],i==n?'\n':' ');
    }
    return 0;

}

蒟蒻加油。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值