• 2013长沙全国邀请赛 总结


    水平太渣. 唯有看完解题报告+标程 AK这套题.

    A CircleGame. 

      需要取上整,核心点加上了一个小于1的小数.

    当n = 2时,有 , 然后可以验证下当n取其他值时也满足。所以就有

    , 则其特征方程的两个解为:

    当 n = 2. 时, 其特征方程为:

    化简得到:

    因为

    所以有:

    S_2 - 2*a*S_1 + (a^2+b) = 0

    得到线性递推关系, 然后矩阵快速幂随便搞搞就好了。

    // Sn = ceil( [a + sqrt(b)]^n ) % m;
    
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    
    typedef long long LL;
    
    struct Matrix{
        LL mat[2][2];
        void zero(){memset(mat,0,sizeof(mat));}
        void unit(){zero();mat[0][0]=mat[1][1]=1;}
    }A,T;
    
    Matrix mult( Matrix a, Matrix b, int mod ){
        Matrix c; c.zero();
        for(int i = 0; i < 2; i++)
        for(int k = 0; k < 2; k++)
        for(int j = 0; j < 2; j++)
        {
            c.mat[i][j] += a.mat[i][k]*b.mat[k][j];
            c.mat[i][j] %= mod;
        }
        return c;
    }
    Matrix Pow( Matrix x, int n, int mod ){
        Matrix c; c.unit();
        while(n){
            if(n&1) c = mult(c,x,mod);
            x = mult(x,x,mod);
            n >>= 1;
        }
        return c;
    }
    
    void init(int a,int b){
        int a1 = 2*a, b1 = b-a*a;
        T.mat[0][0] = 0; T.mat[0][1] = 1;
        T.mat[1][0] = b1; T.mat[1][1] = a1;
        A.zero();
        A.mat[0][0] = 2; A.mat[1][0] = 2*a;
    }
    int main(){
        int a,b,n,m;
        while( scanf("%d%d%d%d",&a,&b,&n,&m) != EOF){
            init(a,b);
            T = Pow( T, n, m );
            A = mult( T, A, m );
            printf("%lld\n", (A.mat[0][0]+m)%m );
        }
        return 0;
    }
    View Code

    D. Hunter

    题意描述有点问题,应该问是否能够拿完全部宝藏,若可以则输出最小花费,否则为0. (事实上不存在宝藏拿不了的数据,保证有解).

    解法是状态压缩. 整体思路:

      因为考虑到宝藏最多13个,则状态数量最多 2^13 = 8*1024, 而且拿宝藏的过程必定是一个一个拿.所以我们可以通过枚举线路.得到最优值.

    状态方程:  dp( mask,  i ) 表示当前状态,0/1分别表示对应宝藏未拿与拿,i表示最后一个拿的i宝藏.

    转移方程:

        dp( mask, j ) = Min{  dp( mask^(1<<j) , i ) + cost( i, j ) } , 其中 cost(i,j)表示从I到J得最小花费.

    初始化, dp( 1<<i, i ) =  Min{ cost( i, 边界 ) }  

    我的做法是 跑了K次SPFA,存储到数组 dist( i, x, y ), 表示从第i个宝藏出发到达点(x,y)的最短距离。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    const int MASK = (1<<13) + 10;
    const int N = 210;
    const int inf = 0x3f3f3f3f;
    int dis[15][N][N];
    bool vis[N][N];
    
    int dp[MASK][15];
    int n, m, K;
    int mp[N][N], cost[15];
    int dir[4][2] = {
    {-1,0},{1,0},{0,-1},{0,1}
    };
    bool legal(int x,int y){
        if( x>=0 && x<n && y>=0 && y<m )
            return true;
        return false;
    }
    struct Point{
        int x,y;
        Point(){}
        Point(int _x,int _y):x(_x),y(_y){}
    }p[15];
    void init(){
        for(int i = 0; i < K; i++){
            queue< Point > Q;
            memset(vis,0,sizeof(vis));
            memset( dis[i], 0x3f, sizeof(dis[i]));
            dis[i][ p[i].x ][ p[i].y ] = 0;
            Q.push( Point(p[i].x, p[i].y) );
            vis[ p[i].x ][ p[i].y ] = true;
            while( !Q.empty() ){
                Point now = Q.front(); Q.pop();
                int x = now.x, y = now.y;
                vis[x][y] = false;
                for(int j = 0; j < 4; j++){
                    int x1 = x+dir[j][0], y1 = y+dir[j][1];
                    if( legal(x1,y1) && (mp[x1][y1]!=inf) && (dis[i][x1][y1] > dis[i][x][y] + mp[x1][y1]) ){
                        dis[i][x1][y1] = dis[i][x][y] + mp[x1][y1];
                        if( !vis[x1][y1] )
                            Q.push( Point(x1,y1) ), vis[x1][y1] = true;
                    }
                }
            }
        }
        for(int i = 0; i < K; i++){
            cost[i] = inf;
            for(int row = 0; row < n; row++)
                cost[i] = min( cost[i], min(dis[i][row][0], dis[i][row][m-1]) );
            for(int col = 0; col < m; col++)
                cost[i] = min( cost[i], min(dis[i][0][col], dis[i][n-1][col]) );
        }
    }
    void solve(){
        memset( dp, 0x3f, sizeof(dp) );
        for(int i = 0; i < K; i++)
            dp[1<<i][i] = cost[i] + mp[ p[i].x ][ p[i].y ];
        int Mask = 1<<K;
        for(int mask = 1; mask < Mask; mask++ ){
            for(int i = 0; i < K; i++){
                for(int j = 0; j < K; j++){
                    if( (mask&(1<<i)) && (mask&(1<<j)) && (i!=j) ){
                        if( dp[mask^(1<<j)][i] < inf )
                            dp[mask][j] = min( dp[mask][j], dp[mask^(1<<j)][i] + dis[i][ p[j].x ][ p[j].y ] );
                    }
                }
            }
        }
        int res = inf;
        for(int i = 0; i < K; i++){
            res = min( res, dp[Mask-1][i] + cost[i] );
        }
        if( res < inf ) printf("%d\n", res );
        else printf("0\n");
    }
    int main(){
        int _;
        scanf("%d", &_);
        while( _-- ){
            scanf("%d%d",&n,&m);
            for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                    scanf("%d", &mp[i][j]);
                    if( mp[i][j] == -1 )
                        mp[i][j] = inf;
                }
            }
            scanf("%d",&K);
            int a,b;
            for(int i = 0; i < K; i++){
                scanf("%d%d",&a,&b);
                p[i] = Point(a,b);
            }
            init();
            solve();
        }
        return 0;
    }
    View Code

    G. Travel_in_Time

    题目描述要看清楚,

    重点: 当前访问顶点u, 下一个顶点v, 需要满足条件 val[u] < val[v] .

    然后就是用Floyd处理任意两点的最短距离,可以看成是经过但不访问的花费。然后之后的dp[u][T]过程就看做是必须访问u点。

    那么还有个问题是,起点S,E不一定要访问,所以需要虚拟一个起点和一个终点。然后随便搞搞就好了。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int N = 110;
    const int M = 310;
    const int inf = 0x3f3f3f3f;
    
    typedef pair<int,int> point;
    #define MIN(a,b) (a)<(b)?(a):(b)
    #define MAX(a,b) (a)>(b)?(a):(b)
    #define min MIN
    #define max MAX
    int dis[N][M];
    int n, m, T, S, E;
    int cost[N], val[N];
    int mp[N][N];
    bool vis[N][M];
    
    void solve(){
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        vis[0][0] = true;
        queue< point > Q;
        Q.push( make_pair(0,0) );
        while( !Q.empty() ){
            point now = Q.front(); Q.pop();
            int u = now.first, t = now.second;
            vis[u][t] = false;
            for(int v = 0; v < n; v++){
                if( v == u ) continue;
                if( mp[u][v] < inf ){
                    int tn = t + mp[u][v] + cost[v];
                    if( (tn <= T) && (dis[v][tn] < dis[u][t] + val[v]) ){
                        if( (v == n-1) || (val[v] > val[u]) ){
                            dis[v][tn] = dis[u][t] + val[v];
                            if( !vis[v][tn] ) // val[v] must greate than zero.
                                Q.push( make_pair(v,tn) ), vis[v][tn] = true;
                        }
                    }
                }
            }
        }
        int res = 0;
        for(int t = 0; t <= T; t++ ){
            res = max( res, max(dis[E][t],dis[n-1][t]) );
        }
        printf("%d\n", res);
    }
    
    int main(){
        freopen("Travel_in_Time.in","r",stdin);
        freopen("test.out","w",stdout);
        int _;
        scanf("%d",&_);
        for(int Case = 1; Case <= _; Case++ ){
            scanf("%d%d%d%d%d",&n,&m,&T,&S,&E);
            S++; E++;
            memset(mp,0x3f,sizeof(mp));
            for(int i = 1; i <= n; i++)
                scanf("%d", &cost[i]);
            for(int i = 1; i <= n; i++)
                scanf("%d", &val[i]);
            int a, b, c;
            for(int i = 0; i < m; i++){
                scanf("%d%d%d",&a,&b,&c);
                a++; b++;
                mp[a][b]=mp[b][a]=min( mp[a][b],c );
            }
            // Floyd
            for(int k = 1; k <= n; k++ )
            for(int i = 1; i <= n; i++ )
            for(int j = 1; j <= n; j++ )
                mp[i][j] = min( mp[i][j], mp[i][k] + mp[k][j] );
            // create virtual point of n;
            for(int i = 1; i <= n; i++){
                if( mp[E][i] < inf )
                    mp[i][n+1] = mp[E][i];
                if( mp[S][i] < inf )
                    mp[0][i] = mp[S][i];
            }
            mp[0][S] = 0; mp[E][n+1] = 0;
            cost[0] = 0; val[0] = -1;
            cost[n+1] = 0; val[n+1] = 0;
            n += 2;
    
            printf("Case #%d:\n", Case);
            solve();
        }
        return 0;
    }
    /*
    1
    4 4 0 0 3
    1 1 1 1
    5 7 9 12
    0 1 1
    1 3 1
    0 2 1
    2 3 1
    */
    View Code

    H.Bottles Arrangement

    证明比较费力, 比赛过程猜结论感觉比较靠谱.

    令 n = 2*L+1,  假定存在一行i, 且 A( i, L+1 ) = m.

    考虑,  A( i, L ) 与 A( i, L+2 ) ,若A(i, L) = m,

    则因为 相邻之差绝对值小于等于1, 就有

    A(i,L-1) >= m-1, A(i, L-2) >= m-2, ..., A(i, 1) >= m-L+1

    A(i,L+2) >= m-1, A(i, L+3) >= m-2, ..., A(i,2L+1) >= m-L

    求和得出 \sum { A(i, j) } >= (2*L+1)*M - L*L = N*M - L*L

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    
    typedef long long LL;
    
    int main(){
        int m, n;
        while( scanf("%d%d",&m,&n) != EOF ){
            int l = n/2;
            printf("%d\n", n*m-l*l );
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Single Number II
    Pascal's Triangle
    Remove Duplicates from Sorted Array
    Populating Next Right Pointers in Each Node
    Minimum Depth of Binary Tree
    Unique Paths
    Sort Colors
    Swap Nodes in Pairs
    Merge Two Sorted Lists
    Climbing Stairs
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3120104.html
Copyright © 2020-2023  润新知