Codeforces #49 div2

本文记录了一场虚拟比赛中五道题目的解题过程与思路。题目包括排序、复杂算法、排列组合、哈希与排序及状态动态规划等问题。文章详细介绍了每道题的解题代码与思考过程。

今天和fwj和小萌virtual了一场比赛,写下解题报告。

这套题也就最后一题有意思点。

A.不解释,排序搞过

#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;

string a[105];
string str,str1;

bool cmp(string x,string y)
{
    return x<y;
}

int main()
{
    int i,j,n,t,t1;
    cin>>str;
    cin>>n;
    for (i=0;i<n;i++)
    {
        cin>>a[i];
    }
    a[n]=str;
    sort(a,a+n+1,cmp);
    for (i=0;i<=n+1;i++)
    {
        if (a[i]==str) break;
    }
    if (i==n+1)
    {
        cout<<str<<endl;
        return 0;
    }
    str1=a[i+1];
    t=str.size();
    t1=str1.size();
    if (t>=t1)
    {
        cout<<str<<endl;
        return 0;
    }
    for (i=0;i<t;i++)
    {
        if (str[i]!=str1[i]) break;
    }
    if (i<t)
    {
        cout<<str<<endl;
        return 0;
    }
    cout<<str1<<endl;
    return 0;
}

B.蛮麻烦的一题,各种YY,小萌赛后给出一个看起来好像很神的方法,明天催他写解题报告……链接如下:http://blog.danc.co/?p=12

我写的很复杂的样子,不看也罢

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int main()
{
    int i,j;
    __int64 t,x,y,ans1,ans2,tt;
    scanf("%I64d%I64d",&x,&y);
    ans1=ans2=0;
    t=1;
    for (i=1;;i++)
    {
        if (x*1.0/t>=0.8 && x*1.0/t<=1.25)
        {
            if (ans1*ans2<x*t)
            {
                ans1=x;
                ans2=t;
            }
            else if (ans1*ans2==x*t && ans1<x)
            {
                ans1=x;
                ans2=t;
            }
        }
        tt=1.25*t;
        if (tt*1.0/t<0.8) continue;
        if (tt<=x)
        {
            if (ans1*ans2<t*tt)
            {
                ans1=tt;
                ans2=t;
            }
            else if (ans1*ans2==t*tt && ans1<tt)
            {
                ans1=tt;
                ans2=t;
            }
        }
        t*=2;
        if (t>y) break;
    }
    t=1;
    for (i=1;;i++)
    {
        if (t*1.0/y>=0.8 && t*1.0/y<=1.25)
        {
            if (ans1*ans2<t*y)
            {
                ans1=t;
                ans2=y;
            }
            else if (ans1*ans2==t*y && ans1<t)
            {
                ans1=t;
                ans2=y;
            }
        }
        tt=t/0.8;
        if (t*1.0/tt<0.8)
        {
            tt--;
        }
        if (t*1.0/tt>1.25) continue;
        if (tt<=y)
        {
            if (ans1*ans2<t*tt)
            {
                ans1=t;
                ans2=tt;
            }
            else if (ans1*ans2==t*tt && ans1<t)
            {
                ans1=t;
                ans2=tt;
            }
        }
        t*=2;
        if (t>x) break;
    }
    printf("%I64d %I64d\n",ans1,ans2);
    return 0;
}

C.想法题,要找一个n的排列让相邻的数之差的绝对值两两不相等。

取一个最大一个最小,然后不断往中间取就行了

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int main()
{
    int i,j,n,p,q;
    scanf("%d",&n);
    p=1;
    q=n;
    for (i=0;i<n-1;i++)
    {
        if (i%2==0) printf("%d ",p++);
        else printf("%d ",q--);
    }
    printf("%d\n",p);
    return 0;
}

D.hash后冒泡排序统计次数。

#include <stdio.h>
#include <string.h>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

int a[305];
int b[305];
int c[305];
map< int,vector<int> > m;
int ans[10000005][2];

int main()
{
    int i,j,n,up;
    scanf("%d",&n);
    for (i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        m[a[i]].push_back(i);
    }
    for (i=0;i<n;i++)
    {
        scanf("%d",&b[i]);
        c[i]=m[b[i]][m[b[i]].size()-1];
        m[b[i]].pop_back();
    }
    up=0;
    for (i=0;i<n;i++)
    {
        for (j=n-1;j>i;j--)
        {
            if (c[j]<c[j-1])
            {
                swap(c[j],c[j-1]);
                ans[up][0]=j-1;
                ans[up++][1]=j;
            }
        }
    }
    printf("%d\n",up);
    for (i=0;i<up;i++)
    {
        printf("%d %d\n",ans[i][0]+1,ans[i][1]+1);
    }
    return 0;
}

E.状态dp。

官方tutorial表示木有看懂,不过自己YY了个三进制的做法,看懂官方题解的求教,谢谢。

题意是求一个图的生成树的个数,要求生成树的度为1的点的个数为k。

dp[i],i表示一个三进制的状态,第k位为0表示这个点还没有连上,为1表示这个点的度为1,为2表示这个店的度大于1

每次转移在状态上找一个没有连上的点y,再寻找一个已经连上的点x,如果这两个点之间存在边就连上,连上之后显然x的度必须大于1,而y的度肯定等于1,转移就出来了。

但是有一个问题,就是判重。

例如1 ---- 2 ---- 3

这么转移会导致先连1--2再连3和先连2--3再连1是不同的结果,因此会造成重复计算。

观察

1------2----3

           |

           4

上图,有可能是先连1-2-4再加上3,可能是先连1-2-3再加上4,也可能是先连2-3-4再加上1,因此可以发现,重复计算的次数是得到的图中入度为1的节点的个数(因为每次加入一个节点入度肯定为1),所以dp[i]最后要除掉入读为1的节点的个数。

代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int map[55][55];
int three[15];
int hash[15];
__int64 dp[100000];
int hash1[15];
int n;

int F(int *a)
{
    int ret=0,i;
    for (i=0;i<n;i++)
    {
        ret=ret+a[i]*three[i];
    }
    return ret;
}

int main()
{
    int i,j,kk,m,tmp,x,y,k,cnt;
    __int64 ans;
    scanf("%d%d%d",&n,&m,&kk);
    memset(dp,0,sizeof(dp));
    three[0]=1;
    for (i=1;i<12;i++)
    {
        three[i]=three[i-1]*3;
    }
    for (i=0;i<m;i++)
    {
        scanf("%d%d",&x,&y);
        map[x-1][y-1]=1;
        map[y-1][x-1]=1;
        dp[three[x-1]+three[y-1]]=2;
    }
    for (i=0;i<three[n];i++)
    {
        if (dp[i]==0) continue;
        tmp=i;
        cnt=0;
        for (j=n-1;j>=0;j--)
        {
            if (tmp>=2*three[j])
            {
                hash[j]=2;
                tmp-=three[j]*2;
            }
            else if (tmp>=three[j])
            {
                hash[j]=1;
                tmp-=three[j];
            }
            else hash[j]=0;
            if (hash[j]==1) cnt++;
        }
        dp[i]/=cnt;
        for (x=0;x<n;x++)
        {
            for (y=0;y<n;y++)
            {
                if (hash[x]==0 || hash[y]!=0 || map[x][y]==0) continue;
                for (k=0;k<n;k++)
                {
                    hash1[k]=hash[k];
                }
                hash1[x]=2;
                hash1[y]=1;
                tmp=F(hash1);
                dp[tmp]+=dp[i];
             //   printf("%d %d\n",tmp,i);
            }
        }
    }
    ans=0;
    for (i=0;i<three[n];i++)
    {
        if (dp[i]==0) continue;
      //  printf("%d %d\n",i,dp[i]);
        tmp=i;
        for (j=n-1;j>=0;j--)
        {
            if (tmp>=2*three[j])
            {
                hash[j]=2;
                tmp-=2*three[j];
            }
            else if (tmp>=three[j])
            {
                hash[j]=1;
                tmp-=three[j];
            }
            else hash[j]=0;
        }
        for (j=0;j<n;j++)
        {
            if (hash[j]==0) break;
        }
        if (j<n) continue;
        cnt=0;
        for (j=0;j<n;j++)
        {
            if (hash[j]==1) cnt++;
        }
        if (cnt==kk) ans+=dp[i];
    }
    printf("%I64d\n",ans);
    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值