中山Jizhongトレーニング1日目のテスト(へま)

T3の画家

説明

ヘクターはたくましい画家、そして非常に= =思考のようなものです

今、これらの柱の年長者によって要求される1からNのラベルに、直線状に配置されたN個のテンプル柱は、塗料の新鮮なコートを描くであろうがあります。ヘクター浴槽がK異なる塗色を有し、塗料はちょうどI CIピラーをバケツペイント、及びでき、C1 + C2 + C3 ... CK = N(即ち、N塗装柱がちょうどすべてのペイントを実行します)。にもかかわらずヘクターの長老は、隣接する列の同じ色をしませ​​必要です。

ヘクターはないだけで、すぐにペイントし始めたが、そのようなどのように多くのペイントプログラム、の合計としていくつかの奇妙な質問に、熟考し始めていなかったと考えたいですか?

すぐヘクターとしてペイントを開始するためには、できるだけ早く彼に答えを教えてください。

入力

正の整数Tの最初の行は、テストデータのセットを表します

テストデータの各セットの場合:

1行目:正の整数K

2行目:Kの正の整数、私はペンキのバケツの列数は、CIを塗装することができます。

出力
、入力データの各セットは、出力ラインプログラムスタッコMOD十億七の数を表す整数の場合。

サンプル入力

3

3

1 2 3

5

2 2 2 2 2

10

1 1 2 2 3 3 4 4 5 5

サンプル出力

10

39480

85937576

データ制約

30%N≤10、T≤5

50%N≤15、T≤5

80%K≤15、Ci≤5、T≤500

100%K≤15、Ci≤6、T≤2000

私はそれが私が放棄したに見えた純粋に数学的な計算だと思いました。

分析

私たちは、さまざまな角度からトピックを理解したいです。我々は最初の石はその後、染めでき柱が配置されています。

私たちは、F [i] [j]がもたらしたすべての後に配置されたI-カラーフロントの柱を示して設定し、Jの柱は、左の列のことで、それらの色が同じである必要があります。

メソッドを充填することは行うことは非常に良いではありませんので、我々は直接ブラシテーブル方式を検討してください。

各色の数が知られているので[I] [j]はF状態ごとに、柱からなるシーケンスの現在の長さの和に、知られています。

i番目の色の列の数は、[i]は、その後されていると[I] FにFから転送された[I + 1]、[I + 1]番目の配列は柱の内部に挿入されているが存在するであろう。

显然,插入时会使原来在一起的两块石柱分开的这种插入会影响到状态本身,所以应该将它单独讨论。

设插入时会插入到p个两边颜色相同的空,q个其他情况的空,那么p+q<=sum+1且p+q<=a[i+1],还剩下a[i+1]-p-q个石柱的插入,它们必然会产生新的颜色相同且相邻的石柱。

那么f[i][j]就可以转移到f[i+1][j-p+a[i+1]-p-q],p与q都可以枚举。将a[i+1]个球放入p+q个空中且不能不放,根据隔板法可知要乘上C(a[i+1]-1,p+q-1).又要在j个两边颜色相同的空中选p个则又要乘上C(j,p),还要在剩下sum+1-j个空中选q个,就还要乘上C(sum+1-j,q)。

直接贴代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#define mod 1000000007
using namespace std;
int C[105][105],f[105][105],T,n,a,sum;
int main()
{
    C[0][0]=1;
    for(int i=1;i<=105;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
        C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);sum=0;f[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a);
            for(int j=0;j<=sum+a+1;j++)f[i][j]=0;
            for(int j=0;j<=sum;j++)
            {
                for(int p=0;p<=min(j,a);p++)
                {
                    for(int q=0;q<=min(sum+1-j,a-p);q++)
                    {
                        if(!q&&!p)continue;
                        long long ans=f[i-1][j];int nex=j-p+a-p-q;
                        (ans*=1ll*C[a-1][p+q-1])%=mod;
                        (ans*=1ll*C[j][p])%=mod;
                        (ans*=1ll*C[sum+1-j][q])%=mod;
                        (f[i][nex]+=ans)%=mod;
                    }
                }
            }
            sum+=a;
        }
        printf("%d\n",f[n][0]);
    }
}

 T2 Vani和Cl2捉迷藏

Description

vani和cl2在一片树林里捉迷藏……

这片树林里有N座房子,M条有向道路,组成了一张有向无环图。

树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。如果从房子A沿着路走下去能够到达B,那么在A和B里的人是能够相互望见的。

现在cl2要在这N座房子里选择K座作为藏身点,同时vani也专挑cl2作为藏身点的房子进去寻找,为了避免被vani看见,cl2要求这K个藏身点的任意两个之间都没有路径相连。

为了让vani更难找到自己,cl2想知道最多能选出多少个藏身点?

Input

第一行两个整数N,M。

接下来M行每行两个整数x、y,表示一条从x到y的有向道路。

Output

一个整数K,表示最多能选取的藏身点个数。

Sample Input

4 4

1 2

3 2

3 4

4 2

Sample Output

2

Data Constraint

对于20% 的数据,N≤10,M<=20。

对于60% 的数据, N≤100,M<=1000。

对于100% 的数据,N≤200,M<=30000,1<=x,y<=N。

虽然不是很懂但还是应该有的分析

选择一个藏身点后就会有一些路径被废掉不能再选。

然后听说这是求DAG的最小不相交路径覆盖?将原图的点拆成x部与y部,若a能到达b则连Xa指向Yb。

然后DAG的最小路径覆盖=原图的结点数-对应二分图的最大匹配数。(????)

个人理解(乱理解):每一条边代表了原图中的一条路径,每一个匹配代表匹配边两边的点被连在一起,即这条路径上有一个点被选择,整条路径将不再可用。因为求的是最大匹配,所以没有匹配的点在原图中就是一条路径的终点,这些点都可作为藏身点,故答案为原图的结点数-对应二分图的最大匹配数。

代码:

 

#include<cstdio>
#include<cstring>
int n,m,ans,mat[205],vis[205],V[205][205];
int solve(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(V[x][i]&&!vis[i])
        {
            vis[i]=1;
            if(!mat[i]||solve(mat[i]))
            {
                mat[i]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++)scanf("%d%d",&u,&v),V[u][v]=1;
    for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
    if(i!=j)V[i][j]=V[i][j]||(V[i][k]&&V[k][j]);
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof vis);
        ans+=solve(i);
    }
    printf("%d\n",n-ans);
}

 T1 水叮当的舞步

Description

水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。

为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~

地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。

水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,地毯左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。

由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

Input

每个测试点包含多组数据。

每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。

接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。

N=0代表输入的结束。

Output

对于每组数据,输出一个整数,表示最少步数。

Sample Input

2

0 0

0 0

3

0 1 2

1 1 2

2 2 1

0

Sample Output

0

3

Data Constraint

对于30%的数据,N<=5

对于50%的数据,N<=6

对于70%的数据,N<=7

对于100%的数据,N<=8,每个测试点不多于20组数据。

分析

没看完虹猫蓝兔的我肯定不会做这个题。

观察数据范围,n最大也就8,直接暴力出奇迹,剪枝后复杂度玄学。

先从小到大枚举最大搜索深度(听说这叫迭代加深?),注意不能二分,因为这是指数级别的复杂度增长。

然后进行两个剪枝,一个是变色前统计一下左上角联通块附近有哪几种颜色,变的时候就只用变成这些颜色。

另一个是当 当前深度 加上 剩下联通块的颜色的种类数  大于我们枚举的最大搜索深度时可以直接return 0(听说这跟A*有关?);

写的时候注意局部变量与全局变量,不能混在一起。

代码:

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,tmp,I,color[15],ma[15][15],vis[15][15],vised[15][15],used[15];
int cnt1(){int ans=0;for(int i=1;i<=6;i++)ans+=(color[i]>0);return ans;}
void cnt2(int x,int y)
{
    if(ma[x][y]==-1||vised[x][y])return;
    if(ma[x][y]){used[ma[x][y]]=1;return;}vised[x][y]=1;
    cnt2(x+1,y);cnt2(x-1,y);cnt2(x,y+1);cnt2(x,y-1);
}
void print(int x,int y,int c,int dep)
{
    if(ma[x][y]==-1||(ma[x][y]!=c&&ma[x][y]!=0)||vised[x][y])return;vised[x][y]=1;
    if(ma[x][y]==c)ma[x][y]=0,color[c]--,vis[x][y]=dep;
    print(x+1,y,c,dep);print(x-1,y,c,dep);print(x,y+1,c,dep);print(x,y-1,c,dep);
}
bool solve(int noww,int t)
{
    if(noww+cnt1()-1>t)return 0;//这个地方的1减去会多搜索,不减不严谨
    if(1==(color[1]>0)+(color[2]>0)+(color[3]>0)+(color[4]>0)+(color[5]>0)+(color[6]>0))return 1;
    memset(vised,0,sizeof vised);memset(used,0,sizeof used);
    I=noww;cnt2(1,1);int u[15]={0};memcpy(u,used,sizeof u);
    for(int i=1;i<=6;i++)
    {   
        if(!u[i])continue;
        memset(vised,0,sizeof vised);
        print(1,1,i,noww);
        if(solve(noww+1,t))return 1;
        for(int i1=1;i1<=n;i1++)for(int j=1;j<=n;j++){if(vis[i1][j]==noww)color[ma[i1][j]=i]++,vis[i1][j]=-1;}
    }
    return 0;
}
int main()
{
    memset(ma,-1,sizeof ma);
    while(scanf("%d",&n)&&n)
    {
        memset(color,0,sizeof color);
        memset(vis,-1,sizeof vis);
        memset(ma,-1,sizeof ma);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&ma[i][j]),color[++ma[i][j]]++;
        for(int i=0;i<=n*n;i++)
        if(solve(0,i)){printf("%d\n",i);break;}
    
    }
}

 

おすすめ

転載: www.cnblogs.com/firecrazy/p/11285601.html