[3.10校内训练赛]

真的报警啦,hzwer又出一堆丧题虐人啦.....

-------------------------------------------

A.[poj-1190]生日蛋糕

要做一个m层的蛋糕,每一层有高度和半径,且要分别比它上面的那一层的高度和半径大至少一,给定总体积n,求最小的侧面和顶上的面积之和m<=20,n<=10000

搜索....但是要加上比较强的剪枝。

1.如果此时的半径和高度无法建出剩余体积那么大的蛋糕,剪掉。这种情况我们不考虑半径和高度的减小,直接用((r-1)^2+(h-1))乘以剩下的层数对比就行了。

2.如果此时剩下的体积最少需要的面积加上此时已经搜到的面积比答案大,剪掉。因为面积和体积关于半径有不同次数的关系,所以我们要尽可能让半径大,整理之后可以得到:如果c+2*Sleft/r>ans,那么剪枝

复杂度O(能过)

#include<iostream>
#include<cstdio>
#define INF 2000000000
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

int ans=INF,n,m;

inline int sqr(int x){return x*x;}

void dfs(int xx,int s,int c,int lastr,int lasth)
{
    if(s<0||c>ans)return;//cout<<"dfs"<<xx<<" "<<s<<" "<<c<<" "<<lastr<<" "<<lasth<<endl;
    if(!xx){if(s==0)ans=min(ans,c);return;}
    if(xx*sqr(lastr-1)*(lasth-1)<s)return;
    for(int i=lastr-1;i>=xx;i--)
        for(int j=lasth-1;j>=xx;j--)
            if(c+2*s/i<ans)
                dfs(xx-1,s-sqr(i)*j,c+2*i*j,i,j);
}

int main()
{
    n=read();m=read();
    for(int i=100;i;i--)
        for(int j=1000;j;j--)
            dfs(m-1,n-sqr(i)*j,i*2*j+i*i,i,j);
    printf("%d",ans==INF?0:ans);
    return 0;
}

B.[百练4119]复杂的整数划分问题

多组数据,每次输入n和k,(n,k<=50)对于每组测试数据,输出以下三行数据: 
第一行: N划分成K个正整数之和的划分数目 
第二行: N划分成若干个不同正整数之和的划分数目 
第三行: N划分成若干个奇正整数之和的划分数目

题解:乱dp呗。n^4

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int f1[55][55][55],f2[55][55][55],f3[55][55][55];
int n,K,num1[55],num2[55],num[55][55];

int main()
{
    f1[0][0][0]=f2[0][0][0]=f3[0][0][0]=1;
    for(int k=1;k<=50;k++)
        for(int i=1;i<=50;i++)
            for(int j=1;j<=i;j++)
            {
                for(int l=0;l<=j;l++)
                {
                    f1[k][i][j]+=f1[k-1][i-j][l];
                    if(l!=j) f2[k][i][j]+=f2[k-1][i-j][l];
                    if(j&1) f3[k][i][j]+=f3[k-1][i-j][l];
                }
                num1[i]+=f2[k][i][j];num2[i]+=f3[k][i][j];num[k][i]+=f1[k][i][j];
            }
    while(scanf("%d %d",&n,&K)!=EOF)
        printf("%d\n%d\n%d\n",num[K][n],num1[n],num2[n]);
    return 0;
}

C.[百练1191]棋盘分割

题目:将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行) 原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。

n<=15

题解:dp,用f[z][i][j][k][l]表示i-j行k-l列那个矩形割z次最小的答案。

复杂度8^4*n

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iomanip>
#define INF 2000000000
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

int n;
double s[15][15];
double ave;
double ss[15][15][15][15][15];

double getans(int xfrom,int xto,int yfrom,int yto)
{
    double h=0;
    for(int i=xfrom;i<=xto;i++)
        h+=s[i][yto]-s[i][yfrom-1];
    return (h-ave)*(h-ave);
}

int main()
{
    n=read();
    for(int i=1;i<=8;i++)
    {
        s[i][0]=0;
        for(int j=1;j<=8;j++)
        {
            double x=read();
            ave+=x;
            s[i][j]=s[i][j-1]+x;
        }
    }
    ave/=(double)n;
    for(int i=1;i<=8;i++)
        for(int j=i;j<=8;j++)
            for(int k=1;k<=8;k++)
                for(int l=1;l<=8;l++)
                    ss[0][i][j][k][l]=getans(i,j,k,l);
   for(int kk=1;kk<=n-1;kk++)
   {
     for(int i=1;i<=8;i++)
        for(int j=i;j<=8;j++)
            for(int k=1;k<=8;k++)
                for(int l=k;l<=8;l++)
                {
                    ss[kk][i][j][k][l]=INF;
                    for(int x=i;x<=j-1;x++)
                    {
                        ss[kk][i][j][k][l]=min(ss[kk][i][j][k][l],ss[kk-1][i][x][k][l]+ss[0][x+1][j][k][l]);
                        ss[kk][i][j][k][l]=min(ss[kk][i][j][k][l],ss[0][i][x][k][l]+ss[kk-1][x+1][j][k][l]);
                    }
                    for(int y=k;y<=l-1;y++)
                    {
                        ss[kk][i][j][k][l]=min(ss[kk][i][j][k][l],ss[kk-1][i][j][k][y]+ss[0][i][j][y+1][l]);
                        ss[kk][i][j][k][l]=min(ss[kk][i][j][k][l],ss[0][i][j][k][y]+ss[kk-1][i][j][y+1][l]);
                    }
                }
   }
   double ans=ss[n-1][1][8][1][8]/(double)n;
   ans=sqrt(ans);
   printf("%0.3lf",ans);
   return 0;
}

D.[百练1724]ROADS

给定一个n个点m条边的图(n<=100,m<=10000),每条边有一定的权值及费用(均<=100)。给定c,求费用不超过c时1到n的最短路长度。

题解:很显然你最多只会走n-1条边,最短路的长度不会超过10000,那么用f[i][j]表示到第i个点,路径长度为k的最小费用。

枚举路径长度(1-10000),枚举边,更新f数组就行了。

oj机子跑得慢,最好优化一下233

复杂度MAXL*m

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#define INF 2000000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

int k,n,m;
struct edge{
    int from,to,w,c;
}e[10005];

bool cmp(edge x,edge y){return x.w<y.w;}
int f[105][10005];

int main()
{
    k=read();n=read();m=read();
    for(int i=1;i<=m;i++)
    {e[i].from=read();e[i].to=read();e[i].w=read();e[i].c=read();}
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)for(int j=0;j<=10000;j++) f[i][j]=INF;
    f[1][0]=0;
    for(int i=1;i<=10000;i++)
    {
        for(int j=1;j<=m;j++)
            if(i>=e[j].w)
            {
                f[e[j].to][i]=min(f[e[j].to][i],f[e[j].from][i-e[j].w]+e[j].c);
            }else break;
        if(f[n][i]<=k)return 0*printf("%d\n",i);
    }
    puts("-1");
    return 0;
}

E.[poj1037]A Decorative fence

给定n和k,求字典序第k小的n的波浪形全排列  n<=20,k是longlong

波浪形全排列:比如13254,任意连续的三个数字中中间会比两边都大或者小。

题解:我们发现一个1-n的全排列确定一项后剩余部分可以看作一个的1到n-1的全排列

用f1[i][j]表示1-i的全排列,第j个开头且先上升的波浪形全排列的个数,同理f2[i][j]表示先下降的,随意dp一下。

最后把每一位数字确定下来。复杂度大概是n^3吧

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int n;
ll K;

ll f1[25][25],f2[25][25];
bool b[25];
int ans[25];

void solve(int xx)
{
    if(xx>n)return;//cout<<"dfs"<<xx<<" "<<K<<endl;
    int num=n-xx+1,nown=0,i;ll tot=0;
    for(i=1;i<=n;i++)
        if(!b[i])
        {
            ++nown;if(xx==2)tot+=i>ans[1]?f1[num][nown]:f2[num][nown];
            else if(i>ans[xx-1]&&ans[xx-1]<ans[xx-2])tot+=f1[num][nown];
            else if(i<ans[xx-1]&&ans[xx-1]>ans[xx-2])tot+=f2[num][nown];
            if(tot>=K)break;else {K-=tot;tot=0;}
        }
    b[i]=1;ans[xx]=i;printf("%d ",i);solve(xx+1);
}

int main()
{
    int t=read();
    f1[1][1]=f2[1][1]=1;
    for(int l=2;l<=20;l++)
        for(int i=1;i<=l;i++)
        {
            for(int j=0;j<i;j++)f1[l][i]+=f2[l-1][j];
            for(int j=l+1;j>i;j--)f2[l][i]+=f1[l-1][j-1];
        }
    while(t--)
    {
        n=read();K=read();memset(b,0,sizeof(b));
        for(int i=1;i<=n;i++)
        if(f1[n][i]+f2[n][i]>=K){b[i]=1;ans[1]=i;printf("%d ",i);solve(2);break;}
        else K-=f1[n][i]+f2[n][i];
        puts("");
    }
}

F.[poj1390]Blocks

题意:有n(n<=200)个1-n的颜色的盒子组成的序列,你每次可以消除一个连续的相同颜色的盒子,得分为数量的平方

给一个解释??

求可能的最大得分

题解:这道题dp的思想非常的妙。

先把相同颜色的连续段缩起来,设len[i]表示第i段的个数。

用f[i][j][k]表示i-j这个区间的盒子,第j个盒子和后面和它颜色相同的k个盒子一起消除能得到的最大得分,那么f[i][j][k]=f[i][j-1][0]+(len[j]+k)^2

然后我们每次处理一个状态时也考虑它和前面的相同颜色盒子一起消除的情况,即枚举一个z,i<=z<j且color[z]=color[j],

那么f[i][j][k]=max(f[i][j][k],f[i][z][k+len[j]]+f[z+1][j-1][0])

复杂度很玄学,理论上不能过,但是就是能过,所以就算O(能过)吧。

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 2000000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

int s[205],len[205];
int f[205][405][205];
int n,tot;
inline int sqr(int x){return x*x;}

int main()
{
    int t=read();
    for(int qq=1;qq<=t;qq++)
    {
        tot=n=read();memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)s[i]=read();
        int j=0;
        for(int i=1;i<=n;i++)
            if(s[i]==s[i-1])
                ++len[j];
            else
                s[++j]=s[i],len[j]=1;
        n=j;
        for(int k=1;k<=n;k++)
            for(int i=1;i+k-1<=n;i++)
            {
                int j=i+k-1;
                for(int l=0;l<=tot-j;l++)
                {
                    f[i][j][l]=max(f[i][j][l],f[i][j-1][0]+sqr(l+len[j]));
                    for(int z=i;z<j;++z)if(s[z]==s[j])
                        f[i][j][l]=max(f[i][j][l],f[i][z][l+len[j]]+f[z+1][j-1][0]);
                }
            }
        printf("Case %d: %d\n",qq,f[1][n][0]);
    }
    return 0;
}

G.[bzoj3172]单词

有一篇文章由n个单词组成,求每个单词在文章中出现的个数。n<=200,总长度<=10^6

一股清流,居然有一道ac自动机模板题....

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int    out[1000005];
int q[1000005],top=0,tail=0;
char s[205][1000005];
int c[1000005][26];
int fail[1000005];
int n,cnt=1;
int nn[1000005];

int check(int num)
{
    int t=1,ans=0;
    for(int i=1;s[num][i];++i)
    {
        int to=s[num][i]-‘a‘;
        while(!c[t][to])t=fail[t];
        t=c[t][to];
    }
    return nn[t];
}

void buildfail()
{
    q[top]=1;
    while(top>=tail)
    {
        int x=q[tail++];
        for(int i=0;i<26;i++)
        {
            if(!c[x][i]) continue;
            int t=fail[x];
            while(!c[t][i])t=fail[t];
            fail[c[x][i]]=c[t][i];out[c[t][i]]++;q[++top]=c[x][i];
        }
    }
    top=-1;tail=0;
    for(int i=1;i<=cnt;i++)if(!out[i])
        q[++top]=i;
    while(top>=tail)
    {
        int x=q[tail++];out[fail[x]]--;nn[fail[x]]+=nn[x];
        if(!out[fail[x]])q[++top]=fail[x];
    }
}

void ins(int num)
{
    int t=1;
    for(int i=1;s[num][i];i++)
    {
        int to=s[num][i]-‘a‘;
        if(!c[t][to]) c[t][to]=++cnt;
        t=c[t][to];
        nn[t]++;
    }
}

int main()
{
    n=read();
    for(int i=0;i<26;i++)c[0][i]=1;fail[1]=0;
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++)ins(i);buildfail();
    for(int i=1;i<=n;i++)printf("%d\n",check(i));
    return 0;
}

除了sb题全是dp,干脆叫dp模拟赛算了。

时间: 2024-10-07 06:47:28

[3.10校内训练赛]的相关文章

[3.16校内训练赛]

这次一个学长出题....结果我把dij写成了大顶的,就说复杂度那么科学怎么T了.........真的丢人 ------------------------------- A.给定一个长度为n的序列,你要求出从那个位置开始连续数n个数,得到的序列最大(先比第一位,再第二位..).n<=2000000 题解:第一眼想到的是可以把每个数拆开来计数排序+dc3后缀数组,应该可过. 但是此题还有一个非常妙的解法. 假设目前最优的开头是i,你要判断开头为j的是否更优,那么你可以找到第一位不同的位k,即s[i

[3.3校内训练赛]

我好菜啊都不会 --------------- A.[百练2812] 恼人的青蛙 给丁一张r*c的图,上面有最多n个青蛙脚印,一个青蛙行走的路线是一条直线,且间隔距离相同,最少行走3个点. 求可能的青蛙走过的踩过最多脚印的路线的脚印数量. r,c,n<=5000 做法:枚举两个点,check一下. 加一些剪枝,比如如果目前这条直线即使可行也不会比答案优秀,我们就可以退出了. 还可以把点按照横坐标排序,每次如果因为横坐标相差过大导致出现剪枝1的情况,就可以不搜前面这个点了,因为后面的点相差显然更大

[3.9校内训练赛]

不要纠结为什么3.9的比赛比3.10的还迟做完,你们首先得知道这场是ditoly出的 ditoly丧病出题人啊丧病出题人啊丧病出题人啊 本机上 T1 std3.000s卡过,我的3.1s被卡了 T3 总共2s,std写的treap跑的飞快,我写的替罪羊2.1s也被卡了 听说学校机子T1只要2s,学校机子真神奇,MBA还是不适合跑程序,低压i5伤不起啊...... 分割线------------------------------------ A.一个n*m的网格图,每个格子都有一个0-9的权值,

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&amp;#39;s problem(manacher+二分/枚举)

pid=5371">HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分同样,第一部分与第二部分对称. 如今给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法.求出以第i个点为中心的回文串长度.记录到数组p中 要满足题目所要求的内容.须要使得两个相邻的回文串,共享中间的一部分,也就是说.左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也

最后一周第二天训练赛之第二题

试题: B - B Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice SPOJ ICODER Description Mathews uses a brand new 16-bit instruction processor. (Yeah i am being sarcastic!). It has one register (say R) and it su

Dream_Chaser队训练赛第一场 I题

Dream_Chaser队训练赛第一场 I题 题目来自2012成都区域赛 I - Count Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4472 Description Prof. Tigris is the head of an archaeological team who is currently in charge of a

2017后期 第 1 场训练赛

题目依次为 NKOJ 上 P3496 P4236 P3774 P2407 1.数三角形 方法很多, 比如推出三边 x y z 的限制关系, 然后加加减减得到计算式子 不过也可以用观察法, 暴力计算出 n 为 1 至 13 对应的结果为: 0 0 0 1 3 7 13 22 34 50 70 95 125 相邻两数差为: 0 0 1 2 4 6 9 12 16 20 25 30 这些相邻两数相邻差又为: 0 1 1 2 2 3 3 4 4 5 5 找到规律了, 如果结果第 i 项为第 i - 1

早晨训练赛第一场 B题 哈希

早晨训练赛第一场 B题 B - Trees in a Row Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 402B Description The Queen of England has n trees growing in a row in her garden. At that, the i-th (1 ≤ i