码迷,mamicode.com
首页 > 其他好文 > 详细

20171208校内训练

时间:2017-12-09 15:57:49      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:一个   暴力   center   多少   printf   程序   tin   矩形   左右   

纹波星(stripe

【题目描述】

Kirby最近要去Ripple Star拜访Ribbon。但是他觉得空手去不太好,所以他决定带一点小礼物。

Kirbyn种颜色的缎带,每种颜色的缎带有ai条,Kirby希望用上所有的缎带来接成一长条来送给Ribbon。为了美观,长条中相邻的两段的颜色不能相同。驾驶Warp Star到达Ripple Star的路程很漫长,闲得无聊的Kirby想算一算他用这些缎带能接出多少不同的长条呢?作为Warp Star上的计算程序的你需要注意的是,Kirby可能还要在长条的一端接上一些小玩意,所以长条是有左右端之分的。

【输入数据】

第一行一个正整数n

第二行n个正整数a1~an,表示各颜色缎带的数量。

【输出数据】

输出只有一行,一个整数表示答案。答案对1000000007取模

【样例输入】

3

1 2 2

【样例输出】

    12

【数据范围】

对于20%的数据,n=2

对于50%的数据,n<=5

另外20%的数据,ai均相同;

对于100%的数据,1<=n<=151<=ai<=5

【样例解释】

“12323”,“13232”,“21323”,“31232”,“23123”,“23132”,“32123”,“32132”,“23213”,“32312”,“23231”,“32321”,共12

题意:给长度为Σai的数列染上n种颜色,每种颜色正好染ai段,相邻段不能染成同色,求方案数。

我们考虑状压,f[i][S][k]表示染了i段,每种颜色剩下的段数状态为S,第i+1段颜色为k的方案数。

显然,f[0][0][x]=1(1<=x<=15)

如何转移?我们枚举第i段染什么颜色(不能与i+1段的重复),f[i][S][i+1段的颜色]+=f[i-1][S‘][你当前枚举的颜色]。(这个位置填你当前枚举的颜色)

这样看起来复杂度是n*Σai*ai^n的。但是我们会发现,有一些状态根本不合法,即i必须=Σ剩余的段数。这些状态根本就不会也不能被搜索到。

但是,如果是普通的DP,这些状态很难不被搜索到(至少代码要写很长)。但是记搜可以很好的处理这一点。

我们可以发现i是不用记录的,只要每种颜色剩下的段数都为0,就是DP的边界。

则时间复杂度为n*n*ai^n。

考虑优化。(为了便于说明,i为上面的i)

考虑更改状态的表示,f[k][S]状态S表示每种段数剩下的颜色数有多少种,k表示当前DP做到的最后一段+1(i+1)的颜色的剩余段数。

为了简单起见,我们把状态改成f[k][a1][a2][a3][a4][a5](a1为段数为1的剩下的颜色数有多少种......)

显然,它是可以像上面一样转移的。

我们枚举第i段染的颜色的剩余段数(以剩余段数为3为例)(可以与i+1段的颜色的剩余段数重复)。

(1)与i+1段重复:f[i+1段的颜色的剩余段数][a1][a2][a3][a4][a5]=f[a3-1][a1][a2+1][a3-1][a4][a5]*(a3-1)(即剩余段数为3的其中一种颜色是和i+1段的颜色一致的,扣除,剩余段数为3的其它颜色这个位置都可以填)

(2)不与i+1段重复:f[i+1段的颜色的剩余段数][a1][a2][a3][a4][a5]=f[a3-1][a1][a2+1][a3-1][a4][a5]*a3(即剩余段数为3的颜色这个位置都可以填)

同样的,f[x][0][0][0][0][0]=1。

这样,时间复杂度就降为ai*ai*n^ai。

技术分享图片
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long mod=1000000007ll;
long long dp[6][16][16][16][16][16];
int s[6];int n;
long long DP(int k,int a[6])
{
    if(a[0]==n)return dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]=1;
    if(dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]!=-1)return dp[k][a[1]][a[2]][a[3]][a[4]][a[5]];
    dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]=0;
    for(int i=1;i<=5;i++)
    {
        if(a[i]==0||(k==i&&a[i]==1))continue;
        a[i]--;a[i-1]++;long long y=DP(i-1,a);a[i]++;a[i-1]--;
        if(k==i)dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]=(dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]+y*(a[i]-1))%mod;
        else dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]=(dp[k][a[1]][a[2]][a[3]][a[4]][a[5]]+y*a[i])%mod;
    }
    return dp[k][a[1]][a[2]][a[3]][a[4]][a[5]];
}
int main()
{
    freopen("stripe.in","r",stdin);freopen("stripe.out","w",stdout);
    memset(dp,-1,sizeof(dp));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int b;scanf("%d",&b);s[b]++;
    }
    printf("%lld",DP(0,s));
    return 0;
}
View Code

切课本上瘾综合征(textbook

【题目描述】

众所周知,小Z喜欢切课本,而且是上瘾的那种。

这一天,小G弄来了一本长为N,宽为M的课本请小Z来切。小G想让小ZP-1刀把课本正好切为P面积相等的矩形,而且每一刀必须把某一块课本切成两块。小Z自然很高兴,但小G还提出了一个要求,就是要使得这P个矩形中,长和宽比值的最大值最小。这让小Z有些犯难,他希望你能帮帮他,让他痛痛快快地切一次课本。

【输入数据】

输入一行三个正整数N,M,P,表示课本的长、宽和切出的矩形个数。

【输出数据】

输出一行一个浮点数,表示最小的长宽最大比值,当你的输出和标准答案不超过10^-6时,你的答案被视为正确答案

【样例输入】

5 4 4

【样例输出】

1.250000000

【数据范围】

对于10%的数据,P=2

对于30%的数据,P<=10

对于60%的数据,P<=15

另外10%的数据,M | NP | (N/M)

对于100%的数据,1<=M<=N<=100001<=P<=20

【样例解释】

技术分享图片

先考虑暴力搜索。对于一块长为a,宽为b的矩形,切p刀。

我们把这个矩形分成两个小矩形,其中一个切p1刀,另一个切p2刀。(枚举p1,p2=p-p1)

考虑横着分成两个矩形和竖着分成两个矩形。

竖着切:显然两个小矩形的长之比为p1:p2。

技术分享图片

横着切同理:宽之比为p1:p2。

但是这样会TLE。我们会发现,这里面有很多相同状态。

考虑记搜。用f[i][j][k]表示当前对于一个长为j,宽为k的矩形切i刀,长宽比的最小值。

但是j是double,k也是double,怎么存到数组里。

map!把这两个double压成一个int。即变为f[i][S](S为map<j,k>)。

这样就可以记搜了。但是map真是慢,不开O2就T飞了。

技术分享图片
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
using namespace std;
typedef pair<double,double> P;
map<P,int> mp;
double f[21][1000000],tot=0;
double Get(double a,double b,int p)
{
    if(a<b)swap(a,b);int now;
    if(!mp[P(a,b)]){mp[P(a,b)]=++tot;now=tot;}
    else now=mp[P(a,b)];
    if(f[p][now])return f[p][now];
    if(p==1)return f[p][now]=a/b;
    double ans1,ans2,ans=999999999;
    for(int p1=1;p1+p1<=p;p1++)
    {
        int p2=p-p1;
        ans1=max(Get(a/p*p1,b,p1),Get(a/p*p2,b,p2));
        ans2=max(Get(a,b/p*p1,p1),Get(a,b/p*p2,p2));
        ans=min(ans,min(ans1,ans2));
    }
    return f[p][now]=ans;
}
int main()
{
    freopen("textbook.in","r",stdin);freopen("textbook.out","w",stdout);
    int n,m,p;scanf("%d%d%d",&n,&m,&p);
    printf("%.15lf",Get((double)n,(double)m,p));cout<<" "<<tot;
    return 0;
}
View Code

 

20171208校内训练

标签:一个   暴力   center   多少   printf   程序   tin   矩形   左右   

原文地址:http://www.cnblogs.com/lher/p/8011557.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!