【刷题】【dp】【贪心】D. Nastya and Scoreboard

题意:(来自谷歌翻译)

  丹尼斯买了鲜花和糖果后(你将在下一个任务中了解这个故事),去和娜斯佳约会,要求她成为一对。现在,他们坐在咖啡馆里,最后……丹尼斯要求她在一起,但是……娜斯佳没有给出任何回答。

  可怜的男孩因此非常沮丧。他很伤心,以至于他用数字打了某种记分牌。数字显示方式与电子钟相同:每个数字位置由 7 段组成,可以打开或关闭以显示不同的数字。图片显示了所有 10 个十进制数字是如何显示的:

  

  打孔后,一些段停止工作,即如果它们更早发光,则某些段可能会停止发光。但丹尼斯记得有多少根棍子在发光,现在又有多少根在发光。丹尼斯准确地打破了 k段,他知道现在哪些棍子正在工作。

  Denis 提出了一个问题:如果您恰好打开 k 棒(现在已关闭),棋盘上可能出现的最大数量是多少? 允许数字包括前导零。

输入:

  第一行包含整数 n ( 1 ≤ n ≤ 2000 ) — 记分板上的位数和 k ( 0 ≤ k ≤ 2000 ) — 停止工作的段数。

  接下来的 n 行包含一个长度为 7 的二进制字符串,其中第 i 编码记分牌的第 i 数字。 记分牌上的每个数字由 7 段组成。我们对它们进行编号,如下图所示,如果第 i 根棍子不发光,则二进制字符串的第 i 位为 0 00,如果它发光,则为 1 。然后一个长度为 7 的二进制字符串将指定现在哪些段正在发光。

  因此,序列“1110111”、“0010010”、“1011101”、“1011011”、“0111010”、“1101011”、“1101111”、“1010010”、“1111111”、“1111011”从 0 开始依次编码所有数字到 9 包括在内。 输出 输出一个由 n 位组成的单个数字 -

例子:

输入

1 7

0000000

输出

8

输入

2 5

0010010 0010010

输出

97

输入

3 5

0100001

1001001

1010011

输出

-1

 

解题思路:

显然可以设计状态 f[i][j] 表示处理完前i个,花费j,得到的最大数字,

记得初始化,则得到 f[n][k] 就是恰好花费k得到的最大数字串

 

然后,我就从wa变成了mle,

压缩状态到 f[2][j] 还在mle

代码如下:

#include<bits/stdc++.h>
using namespace std;
//0-9对应的01字符串,10进制表示 
int str[10]={1110111,10010,1011101,1011011,111010,1101011,1101111,1010010,1111111,1111011};
const int M=128,N=2010;
int str[10]={1110111,10010,1011101,1011011,111010,1101011,1101111,1010010,1111111,1111011};
const int M=128,N=2010;
int mod[2][8];
int add[M][10]; //从原来状态到新状态,需要添加的笔画数 
int sta[N],n,k; //1-n个初始情况,二进制 
int trans_sta[15]; //0-9的数字亮灯情况,转化为二进制 
int ans[N],res;
string f[2][N]; //超内存了... 嘶 

void prepare()
{ 
    mod[0][0]=mod[1][0]=1;
    for(int i=1;i<=7;i++) 
    {
        mod[0][i]=(1<<i);
        mod[1][i]=mod[1][i-1]*10;
    }
    //
    for(int i=0;i<128;i++)
    {
        for(int j=0;j<10;j++)
        {
            for(int p=1;p<=7;p++)
            {
                int a=i%mod[0][p]/mod[0][p-1];
                int b=str[j]%mod[1][p]/mod[1][p-1];
                if(a && !b ) 
                {
                    add[i][j]=-1;
                    break;
                }
                else if(!a && b )
                    add[i][j]++;
            }
        }
    }
    //get_trans_sta[0-9]
    for(int i=0;i<=9;i++)
    {
        for(int p=1;p<=7;p++)
        {
            int b=str[i]%mod[1][p]/mod[1][p-1];
            trans_sta[i]+=b*mod[0][p-1];
        }
    }
}

void work()
{
    res=k;
    for(int i=1;i<=n && res>=0 ;i++)
    {
        int mn=10,pos=0;
        for(int p=9;p>=0;p--)
            if(add[sta[i]][p]!=-1 && add[sta[i]][p]<mn)
                mn=add[sta[i]][p],pos=p;
        res-=mn;
//        cout<<mn<<" "<<res<<endl;
        if(mn==10 || res<0 ) res=-1;
        ans[i]=pos;
    }
}

void init()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        string s; cin>>s;
        for(int j=0;j<7;j++)
            sta[i]=(sta[i]<<1)+s[j]-'0';
    }
}

void Max(string &a,string b)
{
    if(a=="-1" ) 
    {
        a=b;
        return ;
    }
    
    int len=a.length();
    for(int i=0;i<len;i++)
        if(a[i]!=b[i] ) 
        {
            if(a[i]<b[i] ) a=b;
            return ; 
        }
}

void dp()
{
//    for(int i=1;i<=n;i++) printf("%d",ans[i]);
//    printf("\n");
    for(int j=1;j<=k;j++)
        f[0][j]="-1";
    //f[0][0]=""; 
    
    for(int i=1;i<=n;i++)
    {
        int nw=i%2,pre=nw^1;         
        for(int j=0;j<=k;j++)
            f[nw][j]="-1";
        
        for(int p=9;p>=0;p--)
        {
            int cos=add[sta[i]][p];
            if(cos!=-1 )
            {
//                bool pt=false;
//                if(i==n ) pt=true;
                for(int j=cos;j<=k;j++) 
                {
//                    if(cos==0 ) cout<<"  "<<j-cos<<" "<<p<<" "<<f[i-1][j-cos]<<endl;
                    if(f[pre][j-cos]!="-1" ) 
                        Max(f[nw][j],f[pre][j-cos]+(char)(p+'0') ); 
                }
            }
        }
    }
//    for(int i=0;i<=k;i++)
//        cout<<" "<<i<<" "<<f[n][i]<<endl; 
    cout<<f[n%2][k];
}

int main()
{
    prepare();
    init(); 
    work();
    
    if(res<0 ) printf("-1");
    else if(res==0 )
        for(int i=1;i<=n;i++)
            cout<<ans[i];
    else dp();
    return 0;
}
View Code

 

不得已,只能加入贪心的操作:

思路来自:codeforces1341 D. Nastya and Scoreboard(dp + 贪心)_牛客博客 (nowcoder.net)

able[ i ][ j ]表示从第 i 个数字开始到最后,点亮 j 个显示管,能否显示出数字。(从后向前)

现预处理出原始的每个显示屏变到某个数字需要点亮的显示管数,或者不可以变成某个数字

贪心时从前向后,从9到0(保证数字尽量大)

顺利ac,代码如下:

#include<bits/stdc++.h>
using namespace std;
//0-9对应的01字符串,10进制表示 
int str[10]={1110111,10010,1011101,1011011,111010,1101011,1101111,1010010,1111111,1111011};
const int M=128,N=2010;
int mod[2][8];
int add[M][10]; //从原来状态到新状态,需要添加的笔画数 
int sta[N],n,k; //1-n个初始情况,二进制 
int trans_sta[15]; //0-9的数字亮灯情况,转化为二进制 
int ans[N],res;
string f[2][N]; //超内存了... 嘶 

void prepare()
{ 
    mod[0][0]=mod[1][0]=1;
    for(int i=1;i<=7;i++) 
    {
        mod[0][i]=(1<<i);
        mod[1][i]=mod[1][i-1]*10;
    }
    //
    for(int i=0;i<128;i++)
    {
        for(int j=0;j<10;j++)
        {
            for(int p=1;p<=7;p++)
            {
                int a=i%mod[0][p]/mod[0][p-1];
                int b=str[j]%mod[1][p]/mod[1][p-1];
                if(a && !b ) 
                {
                    add[i][j]=-1;
                    break;
                }
                else if(!a && b )
                    add[i][j]++;
            }
        }
    }
    //get_trans_sta[0-9]
    for(int i=0;i<=9;i++)
    {
        for(int p=1;p<=7;p++)
        {
            int b=str[i]%mod[1][p]/mod[1][p-1];
            trans_sta[i]+=b*mod[0][p-1];
        }
    }
}

bool able[N][N];//表示从第i个位置到后面所有的位置,花费j,能否顺利拼成数字串 
void work()// 修改1 
{
    able[n+1][0]=true;
    for(int i=n;i>0;i--)
    {
        for(int p=0;p<=9;p++)
        {
            int cos=add[sta[i]][p];
            if(cos!=-1)
                for(int j=cos;j<=k;j++)
                    if(able[i+1][j-cos] )
                        able[i][j]=true;
        }
    }
}

void init()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        string s; cin>>s;
        for(int j=0;j<7;j++)
            sta[i]=(sta[i]<<1)+s[j]-'0';
    }
}

void Max(string &a,string b)
{
    if(a=="-1" ) 
    {
        a=b;
        return ;
    }
    
    int len=a.length();
    for(int i=0;i<len;i++)
        if(a[i]!=b[i] ) 
        {
            if(a[i]<b[i] ) a=b;
            return ; 
        }
}

void dp()
{
//    for(int i=1;i<=n;i++) printf("%d",ans[i]);
//    printf("\n");
    for(int j=1;j<=k;j++)
        f[0][j]="-1";
    //f[0][0]=""; 
    
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        for(int p=9;p>=0;p--)
        {
            int cos=add[sta[i]][p];
            if(cos==-1 ) continue;
            
            if(sum+cos<=k && able[i+1][k-sum-cos] )//修改2
            {
                cout<<p;
                sum+=cos;
                break;
            }
        }
    }
}

int main()
{
    prepare();
    init(); 
    work();
    
    if(!able[1][k] ) printf("-1");
    else dp();
    return 0;
}

 

同理,也可以用dfs剪枝处理此题,但是个人认为这个思路更好

posted @ 2022-03-22 21:25  心若笺诗  阅读(50)  评论(0编辑  收藏  举报