Noip2015 提高组 Day1

T1神奇的幻方

直通

思路:

  制定一个lrow记录上一个数字所在的行数,lcolume记录上一个数字所在的列数,然后根据题目的描述进行更改即可

上代码:

#include <iostream>
#include <cstdio>
using namespace std;

const int Maxn = 40;
int n,a[Maxn][Maxn];

int main() {
    scanf("%d",&n);
    int mid=n/2+1,Max=n*n;
    int lrow=1,lcolume=mid;
    a[lrow][lcolume]=1;
    for(int i=2; i<=Max; ++i) {
        if(lrow==1 && lcolume!=n)
            lrow=n,a[lrow][++lcolume]=i;
        else if(lrow!=1 && lcolume==n)
            lcolume=1,a[--lrow][lcolume]=i;
        else if(lrow==1 && lcolume==n)
            a[++lrow][lcolume]=i;
        else if(lrow!=1 && lcolume!=n) {
            if(a[lrow-1][lcolume+1]==0)
                a[--lrow][++lcolume]=i;
            else
                a[++lrow][lcolume]=i;
        }
    }
    for(int i=1; i<=n; ++i) {
        for(int j=1; j<=n; ++j)
            printf("%d ",a[i][j]);
        printf("\n");
    }
    return 0;
} 

T2 信息传递

直通

思路:

  讲真这道题是有各种各样的作法...这里给出的是拓扑排序+dfs

上代码:

#include <iostream>
#include <cstdio>

using namespace std;

const int M = 2e5 + 1; 
int n,minn=0x7fffffff;
int t[M],ru[M];
bool v[M];

void topo(int i)
{
    int v=t[i];
    t[i]=0;
    ru[v]--;
    if(!ru[v]) topo(v);
}

void dfs(int x,int steps)
{
    if(v[x])///环完成 
    {
        if(steps<minn) minn=steps;///更新 
        return;
    }
    v[x]=true;
    dfs(t[x],steps+1);
    return;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&t[i]);
        ru[t[i]]++;
    }
    for(int i=1;i<=n;i++)
        if(!ru[i]) topo(i);
    for(int i=1;i<=n;i++)
        if(ru[i] && !v[i]) dfs(i,0);
    printf("%d",minn);
    return 0;
}

T3 斗地主

直通

数据保证:所有的手牌都是随机生成的。

思路:

  首先说在前面,这题有好几种解法....

  什么bfs啊,什么dfs+贪心啊,什么dp啊.

  然而我只会dfs+贪心以及dp版的,但是由于懒嘛,就只写dp版的啦~

  具体完整版请出门右拐通往gaoji大佬的博客园:www.cnblogs.com/zwfymqz/

  搜索斗地主:你会看到你想要的

坑点:

  如上大红字,这就是为什么贪心差不多可以过的原因了吧~

    在做题的时候一定要将循环里的变量搞清楚!这次吃了大亏了...qwq

上代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

using namespace std;

const int M = 24;
int T,n,ans;
///手牌的组数,每组手牌的张数,记录的答案 
int dp[M][M][M][M];
///dp[i][j][k][l]表示打出i套四张,j套三张,k套两张,l张单牌所需要的最少步数
int cnum[M],happens[M/4];
int num[4]={0,5,3,2};

int calc(int one,int two,int three,int four,int king) 
{
    if(king==1)///只出现一张大小王
    {
        one++; ///当作一张单牌
        king--; ///清空 
    }
    if(king==0) ///当做一张单牌来用 
         return dp[four][three][two][one];
    else 
        //return min(dp[four][three][two][one+2],dp[four][three][two+1][one]); 
        return min(dp[four][three][two][one+2],dp[four][three][two][one]+1);
        ///当做2张单牌,或者当做一对对牌 
}

void dfs(int now) ///now是指已经操作的次数 
{
    if(now>=ans) ///最优性剪枝,这个重要 
        return;
    memset(happens,0,sizeof(happens));///清空 
    for(int i=2;i<=14;i++)
        happens[cnum[i]]++; ///记录出现几次的牌有几种 
    ans=min(ans,now+calc(happens[1],happens[2],happens[3],happens[4],cnum[0]));
    for(int k=1;k<=3;k++) ///k表示几顺子
    {
        for(int i=3;i<=14;i++)
        {
            int j;
            for(j=i;j<=14 && cnum[j]>=k;j++)///若可能组成k顺子
            {
                cnum[j]-=k; ///耗费掉了
                if(j-i+1>=num[k]) 
                    dfs(now+1); ///组成k顺子成功!!! 
             } 
             for(j--;j>=i;j--)
                 cnum[j]+=k; ///回溯 
        }
    }
}

int main()
{
    scanf("%d%d",&T,&n);
    memset(dp,1,sizeof(dp));
    ///上面那个其实没什么用...可有可无 
    dp[0][0][0][0]=0;
    ///当每张都不打出去的时候,步数为0 
    for(int i=0;i<=n;i++) ///four 
        for(int j=0;j<=n;j++) ///three 
            for(int k=0;k<=n;k++) ///two 
                for(int l=0;l<=n;l++) ///one 
                    if(i*4+j*3+k*2+l<=n) ///在范围之内
                    {
                        ///赋值为最坏情况:当每张牌都一张一张的打出去
                        ///故将dp数组memset大概是没什么用的 
                        dp[i][j][k][l]=i+j+k+l;
                        if(i)
                        {
                            ///将4张牌进行分解
                            ///(3+1) || (2+2) 
                            dp[i][j][k][l]=min(dp[i][j][k][l],min(dp[i-1][j+1][k][l+1],dp[i-1][j][k+2][l]));
                            ///(2+1+1) || (1+1+1+1) 
                            dp[i][j][k][l]=min(dp[i][j][k][l],min(dp[i-1][j][k+1][l+2],dp[i-1][j][k][l+4]));
                            if(k>=2)
                                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k-2][l]+1),
                                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k-1][l]+1);
                            ///四带一对对牌(一个对牌) 
                            if(l>=2)
                                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k][l-2]+1);
                            ///四带一对单牌(2张单牌) 
                            dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k][l]+1);
                            ///什么都不带
                        }
                        if(j)
                        {
                            ///将3张牌进行分解 
                            ///(2+1) || (1+1+1)
                            dp[i][j][k][l]=min(dp[i][j][k][l],min(dp[i][j-1][k+1][l+1],dp[i][j-1][k][l+3]));
                            if(k)
                                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k-1][l]+1);
                            ///三带一对 
                            if(l)
                                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k][l-1]+1);
                            ///三带一 
                            dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k][l]+1);
                            ///什么都不带 
                        }
                        if(k)
                        {
                            ///将2张牌进行分解 
                            dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k-1][l+2]);
                            ///一对牌 
                            dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k-1][l]+1); 
                        }
                        if(l)///一张单牌 
                            dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k][l-1]+1);
                    }
    while(T--)
    {
        memset(cnum,0,sizeof(cnum));
        ///多组数据 
        ans=n;
        ///最差情况
        int ai,meiyong;
        for(int i=1;i<=n;i++)
        { ///读入数码,以及花色(讲真没啥用) 
            scanf("%d%d",&ai,&meiyong);
            if(ai==1)
                cnum[14]++;
            ///储存A(尖?),把A转化为14数码 
            else
            cnum[ai]++;
            ///其他的按原来数码进行储存(大小王也一样~) 
        }
        dfs(0);
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2017-09-24 18:19  夜雨声不烦  阅读(379)  评论(0编辑  收藏  举报