Codeforces Round #408 (Div. 2) B. C.D.

B:
题目大意:

给你N个盒子,第一个盒子一开始装着骨头,现在有m个位子上有漏洞,如果带有骨头的盒子移动到了这些位子上,那么骨头就会掉在漏洞上再也不会动。

问最终骨头在哪里、

每一次操作交换两个位子上的盒子。

自己的代码被hack了..其实只要动当前为ans的就可以了

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int vis[1000050];
int a[1000050];
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        memset(vis,0,sizeof(vis));
        for(int i=0;i<m;i++)
        {
            int x;
            scanf("%d",&x);
            vis[x]=1;
        }
        int ans=1;
        while(k--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==ans)
            {
                if(vis[x]!=1)ans=y;
            }
            else if(y==ans)
            {
                if(vis[y]!=1)ans=x;
            }
        }
        printf("%d\n",ans);
    }
}

C:

题目链接:http://codeforces.com/contest/796/problem/C


题目的意思是有n个点,n-1条边,现在让你任选一个点当做起点,去掉这个点,然后和这个点连接的所有的点的权值都加一,然后所有和那个点相连的点的点的点权也要加一,有点绕,慢慢理解,1->2->3,就是说去掉123的点权都要加一,然后问你最大集合中的最小点权。

心得:想法其实差不多,但是我wa了..

我打算dfs随便一个mx来比较与cmx ,mx 的距离.但其实这样是不可以的;
比如下面的话应该从2开始走的,
(所以正确的做法应该是按照题解一样,mx 的相邻的节点都应该是mx或者是cmx.)
自己wa的数据
Input

3
2 2 2
3 2
1 2

Participant’s output

4

Jury’s answer

3

参考http://blog.csdn.net/bless924295/article/details/70244270

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
int a[400000];
vector<int >G[400000];
int n;
int solve(){
    int x=0,y=0;
    int maxval=-inf;
    for(int i=1;i<=n;i++){
        if(a[i]>maxval)
            maxval=a[i];
    }
    for(int i=1;i<=n;i++){
        if(a[i]==maxval)
            x++;
        if(a[i]==maxval-1)
            y++;
    }
    int xx=x;
    int yy=y;
    bool flag1=true;
    bool flag2=true;
    for(int i=1;i<=n;i++){
        bool flag3=true;
        if(a[i]==maxval)
            x--;
        else if(a[i]==maxval-1)
            y--;
        for(int j=0;j<(int )G[i].size();j++){
            if(a[G[i][j]]==maxval){
                x--;
                flag3=false;
            }
            if(a[G[i][j]]==maxval-1)
                y--;
        }
        if(x==0){
            flag1=false;
            if(y==0&&flag3)
                flag2=false;
        }
        x=xx,y=yy;
    }
    if(!flag1){
        if(flag2==0)
            return maxval;
        else
            return maxval+1;
    }
    return maxval+2;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=0;i<n-1;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int ans=solve();
    cout<<ans<<endl;
    return 0;
}

D:
参考http://blog.csdn.net/mengxiang000000/article/details/70146788

题目大意:

给你一个树,其中有N个点,N-1条无向边,现在有M个特殊点,问你最多可以切割多少条边,使得每个普通点到最近的特殊点的距离小于等于d.

保证有解。

体悟:一开始以为是树dp,结果怎么想都想不出来..
原来就只是个多点bfs而已…
贪心地使得每个点都被遇见一次…因为是树,如果第二次遇到,那么这条边是不用的,


#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
struct node
{
    int to,pos;
}now,nex;
vector<node >mp[300800];
int vis[300800];
int ans[300800];
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        queue<int >s;
        memset(vis,0,sizeof(vis));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)mp[i].clear();
        for(int i=0;i<m;i++)
        {
            int x;
            scanf("%d",&x);
            s.push(x);
            vis[x]=1;
        }
        for(int i=1;i<=n-1;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            now.to=y;
            now.pos=i;
            mp[x].push_back(now);
            now.to=x;
            now.pos=i;
            mp[y].push_back(now);
        }
        while(!s.empty())
        {
            int u=s.front();
            s.pop();
            for(int i=0;i<mp[u].size();i++)
            {
                int v=mp[u][i].to;
                if(vis[v]==0)
                {
                    ans[mp[u][i].pos]=1;
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
        int cnt=0;
        for(int i=1;i<=n-1;i++)if(ans[i]==0)cnt++;
        printf("%d\n",cnt);
        for(int i=1;i<=n-1;i++)
        {
            if(ans[i]==0)printf("%d ",i);
        }
        printf("\n");
    }
}

E:待补:http://codeforces.com/contest/796/problem/E dp
..

解题报告:

  考虑用f[i][j][x][y]
表示考虑完前i个问题,当前总共用了j次机会,对于第一个人还能看x道题,第二个人还能看y道题的最优值,考虑x和y等于0

的情况,直接转移就好了。

  这里写转出可能会方便一些。

  注意到这个复杂度是O(npk2)
的,但是当p>2∗nk时,可以全选,那么直接全选就好了,特判一下,所以复杂度是O(n2k)。

心得:这个地方还是挺难想到这个状态的。。
要记录左边,右边还可以看多少次。。看了多少次,看到第几题。。。这样的话满足了dp 的要求。。
别人的代码:

    #include<queue>  
    #include<cstdio>  
    #include<string>  
    #include<cstring>  
    using namespace std;  
    int f[2][1005][55][55];  
    int a[1005],b[1005];  
    int main()  
    {  
        int n,p,k;  
        scanf("%d%d%d",&n,&p,&k);  
        if(p>2*(n+k-1)/k)  
            p=2*(n+k-1)/k;  
        int n1,n2,x;  
        scanf("%d",&n1);  
        int i,j;  
        for(i=1;i<=n1;i++)  
        {  
            scanf("%d",&x);  
            a[x]=1;  
        }  
        scanf("%d",&n2);  
        for(i=1;i<=n2;i++)  
        {  
            scanf("%d",&x);  
            b[x]=1;  
        }  
        int y;  
        memset(f,-127/3,sizeof(f));  
        f[0][0][0][0]=0;  
        int p1=1,p2=0;  
        for(i=1;i<=n;i++)  
        {  
            for(j=0;j<=p;j++)  
            {  
                for(x=0;x<k;x++)  
                {  
                    for(y=0;y<k;y++)  
                    {  
                        if(y!=0)  
                            f[p1][j+1][k-1][y-1]=max(f[p1][j+1][k-1][y-1],f[p2][j][x][y]+(a[i]||b[i]));  
                        else  
                            f[p1][j+1][k-1][0]=max(f[p1][j+1][k-1][0],f[p2][j][x][y]+a[i]);  

                        if(x!=0)  
                            f[p1][j+1][x-1][k-1]=max(f[p1][j+1][x-1][k-1],f[p2][j][x][y]+(a[i]||b[i]));  
                        else  
                            f[p1][j+1][0][k-1]=max(f[p1][j+1][0][k-1],f[p2][j][x][y]+b[i]);  

                        if(x!=0&&y!=0)  
                            f[p1][j][x-1][y-1]=max(f[p1][j][x-1][y-1],f[p2][j][x][y]+(a[i]||b[i]));  
                        else if(x!=0)  
                            f[p1][j][x-1][0]=max(f[p1][j][x-1][0],f[p2][j][x][y]+a[i]);  
                        else if(y!=0)  
                            f[p1][j][0][y-1]=max(f[p1][j][0][y-1],f[p2][j][x][y]+b[i]);  
                        else  
                            f[p1][j][0][0]=max(f[p1][j][0][0],f[p2][j][x][y]);  
                    }  
                }  
            }  
            p1=1-p1;  
            p2=1-p2;  
            for(j=0;j<=p;j++)  
                for(x=0;x<k;x++)  
                    for(y=0;y<k;y++)  
                        f[p1][j][x][y]=-100000;  
        }  
        int ans=0;  
        for(j=0;j<=p;j++)  
            for(x=0;x<k;x++)  
                for(y=0;y<k;y++)  
                    ans=max(ans,f[p2][j][x][y]);  
        printf("%d\n",ans);  
        return 0;  
    }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值