zoukankan      html  css  js  c++  java
  • 【纪中受难记】——Day10:逐渐自闭

    只写了一道题,结果可想而知。

    爆零。


    Description

    windy有 N 条木板需要被粉刷。
    每条木板被分为 M 个格子。
    每个格子要被刷成红色或蓝色。
    windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。
    每个格子最多只能被粉刷一次。
    如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?
    一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
     

    Input

    第一行包含三个整数,N M T。
    接下来有N行,每行一个长度为M的字符串,'0'表示红色,'1'表示蓝色。

    Output

    输出一个整数,表示最多能正确粉刷的格子数。
     

    Sample Input

    3 6 3
    111111
    000000
    001100
    

    Sample Output

    16
     

    Data Constraint

     
     

    Hint

    100%的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500 。

     本题使用动态规划解决。

    设sum[i][j]表示第i行中1~j的蓝色格子数,则j-sum[i][j]为红色格子数。

    设g[i][j][k]表示i行中刷j次涂了1~k格的最大收益,则

    考虑在q+1~k之间,

    蓝色格子数为sum[i][k]-sum[i][q],

    红色格子数为k-q-(sum[i][k]-sum[i][q]),

    所以状态转移为:

    g[i][j][k]=g[i][j-1][q]+max(sum[i][k]-sum[i][q],k-q-(sum[i][k]-sum[i][q])),

    则每行收益为g[i][j][m],k为粉刷次数。

    所以可以转化成一个背包问题解决。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=55;
     4 int n,m,t;
     5 int sum[N][N],g[N][N*N][N],f[N][N*N];
     6 int main(){
     7     scanf("%d%d%d",&n,&m,&t);
     8     char c[500];
     9     for(int i=1;i<=n;i++){
    10         cin>>c;
    11         for(int j=1;j<=m;j++){
    12             if(c[j-1]=='1') sum[i][j]=sum[i][j-1]+1;
    13             else sum[i][j]=sum[i][j-1];
    14         }
    15     }
    16     for(int i=1;i<=n;i++){
    17         for(int j=1;j<=m;j++){
    18             for(int k=1;k<=m;k++){
    19                 for(int q=j-1;q<k;q++){
    20                     int tm=max(sum[i][k]-sum[i][q],k-q-(sum[i][k]-sum[i][q]));
    21                     g[i][j][k]=max(g[i][j][k],g[i][j-1][q]+tm);
    22                 }
    23             }
    24         }
    25     }
    26     for(int i=1;i<=n;i++){
    27         for(int j=1;j<=t;j++){
    28             for(int k=0;k<=min(j,m);k++){
    29                 f[i][j]=max(f[i][j],f[i-1][j-k]+g[i][k][m]);
    30             }
    31         }
    32     }
    33     int ans=0;
    34     for(int i=1;i<=t;i++){
    35         ans=max(f[n][i],ans);
    36     }
    37     printf("%d",ans);
    38 //    for(int i=1;i<=n;i++){
    39 //        for(int j=1;j<=m;j++){
    40 //            printf("%d ",sum[i][j]);
    41 //        }
    42 //        cout<<endl;
    43 //    }
    44     return 0;
    45 }

    Description

    windy在有向图中迷路了。
    该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
    现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
    注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
     

    Input

    第一行包含两个整数,N T。
    接下来有 N 行,每行一个长度为 N 的字符串。
    第i行第j列为'0'表示从节点i到节点j没有边。
    为'1'到'9'表示从节点i到节点j需要耗费的时间。

    Output

    输出一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。
     

    Sample Input

    2 2
    11
    00
    

    Sample Output

    1
     

    Data Constraint

     
     

    Hint

    100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。

     送分题,对于一个点,我们可以将它拆成9个点,相应的矩阵变成9*n长度,对于一条边(u,v),我们可以先处理出u到u后面8个节点的路径,每当一条边连通,相当于将缺少的最后一条边补上,这条边才算真正连通,(设置中间那些节点对答案并无影响)接着就可以矩阵乘法了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=100;
     4 const int mod=2009;
     5 int n,t;
     6 int cnt,x;
     7 struct matrix{
     8     int a[N][N];
     9     void Empty(){
    10         memset(a,0,sizeof(a));
    11     }
    12     void One(){
    13         Empty();
    14         for(int i=1;i<=cnt;i++) a[i][i]=1; 
    15     }
    16     matrix operator *(matrix b){
    17         matrix tmp;
    18         tmp.Empty();
    19         for(int i=1;i<=cnt;i++){
    20             for(int j=1;j<=cnt;j++){
    21                 for(int q=1;q<=cnt;q++){
    22                     tmp.a[i][j]+=(a[i][q]*b.a[q][j])%mod;
    23                     tmp.a[i][j]%=mod;
    24                 }
    25             }
    26         }
    27         return tmp;
    28     }
    29     void out(){
    30         for(int i=1;i<=cnt;i++){
    31             for(int j=1;j<=cnt;j++){
    32                 printf("%d ",a[i][j]);
    33             }
    34             printf("\n");
    35         }
    36     }
    37 }res,ans;
    38 void matrix_pow(int Q){
    39     ans.One();
    40     while(Q){
    41         if(Q&1){
    42             ans=ans*res;
    43         }
    44         res=res*res;
    45         Q>>=1;
    46     }
    47 }
    48 int pos(int a1,int a2){
    49     return a1+a2*n;
    50 }
    51 int main(){
    52     freopen("ans.txt","w",stdout);
    53     scanf("%d%d",&n,&t);
    54     res.Empty();
    55     cnt=n*9;
    56     for(int i=1;i<=n;i++){
    57         for(int j=1;j<=8;j++){
    58             res.a[pos(i,j)][pos(i,j-1)]=1;
    59         }
    60         for(int j=1;j<=n;j++){
    61             scanf("%1d",&x);
    62             res.a[i][pos(j,x-1)]=1;
    63         }
    64     }
    65     res.out();
    66     matrix_pow(t);
    67     printf("%d",ans.a[1][n]);
    68     return 0;
    69 }

    Description

    windy学会了一种游戏。 对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。 最开始windy把数字按顺序1,2,3,……,N写一排在纸上。 然后再在这一排下面写上它们对应的数字。 然后又在新的一排下面写上它们对应的数字。 如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下


    1 2 3 4 5 6


    2 3 1 5 4 6


    3 1 2 4 5 6


    12 3 5 4 6


    2 3 1 4 5 6


    3 1 2 5 4 6


    1 2 3 4 5 6


    这时,我们就有若干排1到N的排列,上例中有7排。 现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。

     

    Input

    一个整数,N。

    Output

    一个整数,可能的排数。

     

    Sample Input

    3

    Sample Output

    3
     

    Data Constraint

     
     

    Hint

    100%的数据,满足 1 <= N <= 1000 。

    这题需要一点点思考。

    根据题意,我们发现给的变换中有“环”出现,比如1->2,2->3,3->1构成一个三元环,因此,排数就等于所有环的最小公倍数+1。

    考虑到长度为1并不构成环,对题目没有影响,而出现两个含有倍数关系的环,实质可以拆成两个不同质数,但如果出现指数关系,公倍数就是大的那个,因此用背包模型来解决。

    我们先筛出质数表,n为背包容量,每个质数及其指数倍为物品,数的大小为物品的体积,求方案数即可。

    注意开长整型。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const ll N=1010;
     5 bool check[N];
     6 ll cnt,p[N],n,ans;
     7 ll f[N];
     8 void Eus(ll n){
     9     check[0]=check[1]=1;
    10     for(int i=1;i<=n;i++){
    11         if(!check[i]) p[++cnt]=i;
    12         for(int j=1;j<=cnt;j++){
    13             if(i*p[j]>n) break;
    14             check[i*p[j]]=1;
    15             if(i%p[j]==0) break;
    16         }
    17     }
    18     
    19 }
    20 int main(){
    21     scanf("%lld",&n);
    22     Eus(n);
    23     f[0]=1;
    24     for(ll i=1;i<=cnt;i++){
    25         for(ll j=n;j>=p[i];j--){
    26             for(ll k=p[i];k<=j;k*=p[i]){
    27                 f[j]+=f[j-k];
    28             }
    29         }
    30     }
    31     ans=0;
    32     for(int i=0;i<=n;i++) ans+=f[i];
    33     printf("%lld",ans);
    34     return 0;
    35 }

    Description

    windy定义了一种windy数。
    不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。
    windy想知道,在A和B之间,包括A和B,总共有多少个windy数?
     

    Input

    两个整数,A B。

    Output

    一个整数,表示A~B中有多少个windy数。
     

    Sample Input

    1 10

    Sample Output

    9
     

    Data Constraint

     
     

    Hint

    100%的数据,满足 1 <= A <= B <= 2000000000 。

     

    数位dp板子,可惜我太辣鸡了,没学过。

    学了之后发现好简单。

    设f[i][j]表示第i位填j的windy数数量。

    先递推出来,然后处理问题。

    我们只要分别求出1~a与1~b的windy数数量再相减即可。

    引用:

    y2823774827y巨佬的题解

    将数字分为三个部分,一个是长度不为len的部分,只要差大于等于2随便填,一个是有len位但是最高位不到len位数字的部分,一个是有len位且最高位为len的数字,但次位尽量往高位贴的部分,因为答案包括边界,所以是solve(r+1)-solve(l)。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 ll a,b;
     5 ll f[15][10];
     6 ll num[20];
     7 ll solve(ll x){
     8     memset(num,0,sizeof(num));
     9     ll len=0,ans=0;
    10     while(x){
    11         num[++len]=x%10;
    12         x/=10;
    13     }
    14     for(ll i=1;i<len;i++){
    15         for(ll j=1;j<=9;j++){
    16             ans+=f[i][j];
    17         }
    18     }
    19     for(ll i=1;i<num[len];i++) ans+=f[len][i];
    20     for(ll i=len-1;i>=1;i--){
    21         for(ll j=0;j<num[i];j++){
    22             if(abs(j-num[i+1])>=2) ans+=f[i][j];
    23         }
    24         if(abs(num[i]-num[i+1])<2) break;
    25     }
    26     return ans;
    27 }
    28 int main(){
    29     scanf("%lld%lld",&a,&b);
    30     for(ll i=0;i<=9;i++) f[1][i]=1;
    31     for(ll i=2;i<=10;i++){
    32         for(ll j=0;j<=9;j++){
    33             for(ll k=0;k<=9;k++){
    34                 if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
    35             }
    36         }
    37     }
    38     printf("%lld",solve(b+1)-solve(a));
    39     return 0;
    40 }

     总结:知识要扎实的学习。

    ——抓住了时间,却不会利用的人,终究也逃不过失败的命运。
  • 相关阅读:
    Index(4.3)
    第七次会议(4.22)
    第六次会议(4.15)
    第五次会议(4.8)
    第四次会议(4.2)
    第三次会议(3.25)
    第二次扩大会议(3.19)
    第二次会议(3.25)
    第一次会议(3.11)
    牛客练习赛25A求1-x因数和(离散求和)
  • 原文地址:https://www.cnblogs.com/Nelson992770019/p/11329582.html
Copyright © 2011-2022 走看看