2019 Multi-University Training Contest 8 部分补题

2019 Multi-University Training Contest 8 部分补题

还是老方式,把自己比赛没A过的题,用自己思路敲一边,该补的题补一下。

这场比赛吧…感觉自己带节奏了,要不然矩形联通块那题应该能早A一个小时。

因为堕落重温了一部哈利波特电影,导致补题晚了3小时
结束时间:2019-8-17 凌晨1:19

1003:Acesrc and Good Numbers

题意:题意求 &lt; = x &lt;=x <=x最大的 n n n,满足1−n中的所有数的数位中数 d d d 出现n次

​ 题解证明了对于d=1…9,f(d,x)的值不会超过 1 0 11 10^{11} 1011 ,其中的证明自己不太能看懂。这题正解可以暴力打表,打表过程中可以剪枝。自己也看了很多博客,都是前几项打表,然后在OEIS中找数列(比赛过程中哪能看什么网站。

所以

思路:无(其实打表可以正着O(n)打表,也可以写个 l o g 10 n log_{10}n log10n 复杂度的计算1~n中数位d出现的总和的函数。

代码:无


1006:Acesrc and Travel

题意:nothing

思路:nothing

代码:nothing


1008:Andy and Maze

题意:你迷失在了一个迷宫里,这里有n个房间,共有m条无向边连接两个房间(三无图),每个房间有一颗宝石,没经过一条边e,都要花费e.w的时间,现在你并不知道自己在哪个房间里,但是你必须找到k颗宝石才能走出走出迷宫,不能重复经过同一个房间,问收集到k颗宝石你需要最大需要的时间是多少?

总结:k个顶点的简单路径和的最大值。

思路:暴力搜索+剪纸竟然过了,当然后来出题人说数据水了,正解是一种随机算法,使用color coding的思想。

暴力剪枝代码:(数据水过的

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long u64;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=1e4+10;
/*暴力+剪纸*/
int n,m,k,ans,mx;
vector<P> g[N];
bool vis[N];
void dfs(int u,int cnt,int sum)
{
    if(vis[u]) return ;
    if(sum+(k-cnt)*mx <=ans) return ;
    vis[u]=1;
    if(cnt==k){
        ans=max(ans,sum);
        vis[u]=0;
        return ;
    }
    for(P &e:g[u]){
        int v=e.first,w=e.second;
        dfs(v,cnt+1,sum+w);

    }
    vis[u]=0;

}
int main()
{
    int t ;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        mx=ans=-1;
        for(int i=1;i<=n;++i) g[i].clear();
        for(int i=0;i<m;++i){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            mx=max(mx,w);
            g[u].push_back({v,w});
            g[v].push_back({u,w});
        }
        mset(vis,0);
        for(int i=1;i<=n;++i){
            dfs(i,1,0);
        }
        if(ans==-1) printf("impossible\n");
        else printf("%d\n",ans);

    }
}

下面来讲下正解color coding的思想。

我们对图中的点随机染色,颜色的种类是k种(为选取的简单路径的顶点个数)

我们接下来进行dp, d p [ l s ] [ v ] [ s ] dp[ls][v][s] dp[ls][v][s] 代表长度为ls的路径,结尾顶点是v,此条路径颜色集合为s的最大时间。那么 d p [ 1 ] [ v ] [ 1 &lt; &lt; c o l o r [ v ] ] = 0 dp[1][v][1&lt;&lt;color[v]]=0 dp[1][v][1<<color[v]]=0 ,其他的 d p [ 1 ] [ ∗ ] [ ∗ ] = − i n f dp[1][*][*]=-inf dp[1][][]=inf(无效值)

而这个算法要求路径的所有顶点颜色不同。

我们枚举路径长度ls,对于其中一个状态 d p [ l s ] [ v ] [ s ] ​ dp[ls][v][s ]​ dp[ls][v][s],枚举v的临界顶点u,那么 d p [ l s ] [ v ] [ s ] = m a x ( d p [ l s ] [ v ] [ s ] , d p [ l s − 1 ] [ u ] [ s   X o r   ( 1 &lt; &lt; c o l o r [ v ] ) ] ) ​ dp[ls][v][s]=max(dp[ls][v][s],dp[ls-1][u][s\ Xor\ (1&lt;&lt;color[v])])​ dp[ls][v][s]=max(dp[ls][v][s],dp[ls1][u][s Xor (1<<color[v])]) ,且要求 s &amp; ( 1 &lt; &lt; c o l o r [ v ] ) ! = 0 ​ s\&amp;(1&lt;&lt;color[v])!=0​ s&(1<<color[v])!=0

我们枚举到答案的概率为 k ! / k k k!/k^k k!/kk。故我们只需要 T ∗ ( k k / k ! ) T*(k^k/k!) T(kk/k!)次遍历这个算法,T越大,就越有可能获得到答案那条路径。

但是这每一次的dp计算时间复杂度还是太大,为 O ( k ∗ 2 k ∗ m ) O(k*2^k*m) O(k2km) , 但细心的话会发现,求 d p [ l s ] [ v ] [ s ] dp[ls][v][s] dp[ls][v][s] 是通过 d p [ l s − 1 ] [ u ] [ s   X o r   ( 1 &lt; &lt; c o l o r [ v ] ) ] ) dp[ls-1][u][s\ Xor\ (1&lt;&lt;color[v])]) dp[ls1][u][s Xor (1<<color[v])])来求的,而 [ s   X o r   ( 1 &lt; &lt; c o l o r [ v ] ) ] ) [s\ Xor\ (1&lt;&lt;color[v])]) [s Xor (1<<color[v])]) 总是比s小的。所以我们可以省略第一维的ls,只需从小到大枚举s,对于每一个s枚举所有的顶点v,然后求 d p [ s ] [ v ] dp[s][v] dp[s][v]即可,这样的时间复杂度为 O ( 2 k ∗ m ) O(2^k*m) O(2km)

color coding k-path近似算法详解

代码:

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long u64;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=1e4+10;
int n,m,k;
vector<P> g[N];
int color[N],dp[80][N];//s,v
void init(int n)
{
    for(int i=1; i<=n; ++i) color[i]=rand()%k;
}
int solve(int n,int m,int k)
{
    init(n);

    int top=1<<k;
    for(int i=1; i<=n; ++i)
    {
        for(int s=0; s<top; ++s) dp[s][i]=-inf; // 无效值
        dp[1<<color[i]][i]=0;
    }
    for(int s=0; s<top; ++s)
    {
        for(int u=1; u<=n; ++u) //枚举顶点
        {
            if(!(s&(1<<color[u]))) continue;//不符合
//                cout<<"asdasd"<<endl;
            for(P &p:g[u])
            {
                int v=p.first,w=p.second;
                dp[s][u]=max(dp[s][u],dp[s^(1<<color[u])][v]+w);
            }
        }
    }
    int ans=-inf;
    for(int i=1; i<=n; ++i) ans=max(ans,dp[top-1][i]);
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    srand(time(NULL));
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>k;
        for(int i=1; i<=n; ++i) g[i].clear();
        for(int i=0; i<m; ++i)
        {
            int u,v,w;
            cin>>u>>v>>w;
            g[u].push_back({v,w});
            g[v].push_back({u,w});
        }
        int p=300;//随机300次
        int ans=-inf;
        while(p--)
            ans=max(ans,solve(n,m,k));
//        cout<<"ans:"<<ans<<endl;
        if(ans<0)
            cout<<"impossible"<<endl;
        else
            cout<<ans<<endl;
    }
    return 0;
}


1009:Calabash and Landlord

题意:给你两个矩形,每个矩形给出其左下角坐标和右上角坐标,问矩形把所以所有格子分成几个联通块。

思路:坐标离散化+dfs。需要注意的是,对于矩形的边界需要格外处理。我们把矩形边界只在偶数坐标出现,奇数坐标看成格子即可。

代码

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long u64;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=20;
int X[10],Y[10];//x1, x2,
int sa[N];
void disperse(int X[],int n)
{
    int top=0;
    for(int i=0; i<n; ++i)
    {

        sa[top++]=X[i];
        sa[top++]=X[i]+1;
    }
    sort(sa,sa+top);
    top=unique(sa,sa+top)-sa;
    for(int i=0; i<n; ++i)
    {
        int th=lower_bound(sa,sa+top,X[i])-sa;
        X[i]=th+1;
    }
}
int grid[20][20];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
void dfs(int i,int j)
{
    grid[i][j]=2;
    for(int k=0;k<4;++k){
        int x=i+dir[k][0];
        int y=j+dir[k][1];
        if(x>0&&x<N&&y>0&&y<N&&grid[x][y]!=1){//如果有墙就不能过去
            x+=dir[k][0];
            y+=dir[k][1];
            if(x>0&&x<N&&y>0&&y<N&&grid[x][y]!=2)//没墙但走过也不能过去
                dfs(x,y);
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        for(int i=0; i<4; ++i)
            cin>>X[i]>>Y[i];
        disperse(X,4);
        disperse(Y,4);
        mset(grid,0);
        for(int i=2*X[0];i<=2*X[1];++i)
        {
            grid[i][2*Y[0]]=1;
            grid[i][2*Y[1]]=1;
        }
        for(int i=2*X[2];i<=2*X[3];++i)
        {
            grid[i][2*Y[2]]=1;
            grid[i][2*Y[3]]=1;
        }
        for(int j=2*Y[0];j<=2*Y[1];++j){
            grid[2*X[0]][j]=1;
            grid[2*X[1]][j]=1;
        }
        for(int j=2*Y[2];j<=2*Y[3];++j){
            grid[2*X[2]][j]=1;
            grid[2*X[3]][j]=1;
        }
        int ans=0;
        for(int i=1;i<N;i+=2){
            for(int j=1;j<N;j+=2){
                if(grid[i][j]==0){
                    ans+=1;
                    dfs(i,j);
                }
            }
        }
        cout<<ans<<endl;
    }
}

1010:Quailty and CCPC(水题)

题意:共有n个队伍参加比赛,给你一个n,d和n个队伍的信息。令 p = n ∗ 10 d % p=n*10d\% p=n10d%,如果p的小数部分是0.5那就排名输出第p+1队伍的名字,否则输出Quailty is very great。

思路 n ∗ d % 10 = 5 n*d\%10=5 nd%10=5,则输出排序后第 ( n ∗ d ) / 10 + 1 (n*d)/10+1 (nd)/10+1个队伍名称

代码

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long u64;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=1e5+10;
struct Node
{
    string name;
    int cnt,time;
    bool operator <(const Node& other ) const
    {
        if(cnt!=other.cnt) return cnt>other.cnt;
        return time<other.time;
    }
}node[N];
int  main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    int t,n,d;
    cin>>t;
    while(t--)
    {
        cin>>n>>d;
        for(int i=0;i<n;++i) cin>>node[i].name>>node[i].cnt>>node[i].time;
        if(n*d%10!=5){
            cout<<"Quailty is very great"<<endl;
        }
        else{
            int th=n*d/10+1;
            nth_element(node,node+th-1,node+n);
            cout<<node[th-1].name<<endl;
        }
    }
    return 0;
}


1011 :Roundgod and Milk Tea

因为马虎,补题过程中wa了一发(应该是先减再赋为0,写成先赋为0,再相见

题意:现在有n个队伍,每个队伍有ai,并且造出了bi杯奶茶。现在要和奶茶,规则是每个队伍不可以和自己造的奶茶,问最后可以又多少个人喝到奶茶。

思路:我们这个让这n个队伍,倒着喝奶茶,即品茶人的顺序是1~n,喝奶茶的顺序是n~1, 这样只会有一个地方出现交叉,我们只要特殊判断处理这个地方即可。

代码

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long u64;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=1e6+10;
ll A[N],B[N];
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    ll t,n;
    cin>>t;
    while(t--)
    {
        ll ans=0;
        cin>>n;
        for(ll i=1; i<=n; ++i)
            cin>>A[i]>>B[i];
        ll r=n;
        for(ll i=1; i<=n&&r>=1; ++i)
        {
            while(A[i]>0&&r>=1)
            {
                if(i==r)//处理交叉情况
                {
                    for(ll k=r-1; k>=1&&A[i]!=0; --k)
                    {
                        if(A[i]>=B[k])
                        {
                            ans+=B[k];
                            A[i]-=B[k]; 
                            B[k]=0;
                        }
                        else
                        {
                            ans+=A[i];
                            B[k]-=A[i];
                            A[i]=0;
                            break;
                        }
                    }
                    A[i]=0;
                }
                else if(A[i]>=B[r])
                {
                    ans+=B[r];
                    A[i]-=B[r];
                    B[r]=0;
                    --r;
                }
                else
                {
                    ans+=A[i];
                    B[r]-=A[i];
                    A[i]=0;
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值