2013 Aizu Regional Contest(UValive 6661,6662,6663,6664,6665,6669)

这篇博客详细解析了2013年Aizu区域竞赛中的六道题目,包括暴力和动态规划的解题方法。题目涉及集合求解、蚂蚁行走模拟、区域计数、时钟角度计算、龙的甜甜圈问题以及隐藏的树。文章通过代码示例阐述了各种问题的解决思路,展示了如何运用康拓展开、优先队列和BFS等算法技巧。
摘要由CSDN通过智能技术生成

A - Equal Sum Sets

题意:

输入三个数 n, k, s .
求有多少种集合元素个数为k,元素最大值为n,元素之和为s,集合中元素均不相同.

思路:

  1. 暴力
    由于 n20 ,那么只有 220=106 种集合,那么枚举集合判断是否符合条件即可,复杂度为 O(2nk) ,但由于有100组样例。。所以只能很勉强地过,n 再大一点就得跪。
    并不推荐这种做法。。
  2. DP
    dp[i][j][k]表示 n = i , k = j , s = k 的集合数,dp[i][j][k]可以由两种状态转移得到,一种是集合中有元素 i 的,一种是没有的。
    dp[i][j][k]=dp[i1][j][k]+dp[i1][j1][ki]

代码1(暴力):

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n , k , s ;
    while ( cin >> n >> k >> s ) {
        if ( !n && !k && !s ) break ;
        int total = ( 1 << n ) ;
        int ans = 0 ;
        for ( int i = 0 ; i < total ; i++ ) {
            int tmp = i , sum = 0 ;
            int a = 1 , cnt = 0 ;
            while ( tmp ) {
                if ( tmp & 1 ) {
                    sum += a ;
                    cnt++ ;
                    if ( sum > s || cnt > k ) break ;
                }
                a++ ; tmp >>= 1 ;
            }
            if ( sum == s && cnt == k ) ans ++ ;
        }
        cout << ans << endl ;
    }
    return 0;
}

代码2(DP):

#include<bits/stdc++.h>
using namespace std;

int n , k , s ;
int dp[22][12][200] ;

void init()
{
    dp[0][0][0] = 1 ;
    for ( int i = 1 ; i <= 20 ; i++ ) 
        for ( int j = 0 ; j <= 10 ; j++ ) 
            for ( int k = 0 ; k <= 160 ; k++ ) 
            {
                dp[i][j][k] = dp[i-1][j][k] ;
                if ( j && k >= i )
                    dp[i][j][k] += dp[i-1][j-1][k-i] ;
            }
}
int main()
{
    init() ;
    while ( ~scanf( "%d%d%d" ,&n ,&k ,&s ) ) 
    {
        if ( !n && !k && !s ) break ;
        printf( "%d\n" , dp[n][k][s] ) ;
    }
    return 0;
}

B - The Last Ant

题意:

输入两个数 n , l .
n 代表蚂蚁的数量,l 代表木板的长度,接下来输入 n 只蚂蚁所在的位置与行动的方向。
若两只蚂蚁在相邻的两格之间相遇,则掉头而行;若相遇位置在一格中心,则继续沿原方向前进。
已知蚂蚁速度为 1 格/s,求所有蚂蚁都掉下木板所需的时间,并求出最后一个离开木板的蚂蚁编号。

思路:

模拟,模拟,模拟。。

代码:

#include<bits/stdc++.h>
using namespace std;

struct Ant{
    int id, pos, leave;
    char dir[2] ;
    bool operator < ( const Ant &a )const {
        return pos < a.pos ;
    }
};
Ant a[25] ;
int n , l ;
void turn( int i , int j ) {
    if ( a[i].dir[0] == 'L' ) a[i].dir[0] = 'R' ;
    else a[i].dir[0] = 'L' ;
    if ( a[j].dir[0] == 'L' ) a[j].dir[0] = 'R' ;
    else a[j].dir[0] = 'L' ;
}
void work()
{
    int time = 0 ;
    int out = 0 ;
    Ant last[2] ;
    while ( 1 ) {
        time ++ ;
        for ( int i = 0 ; i < n ; i++ ) {
            if ( a[i].pos <= 0 || a[i].pos >= l ) continue ;
            if ( a[i].dir[0] == 'R' ) a[i].pos++ ;
            else a[i].pos-- ;
            if ( a[i].pos == 0 || a[i].pos == l ) {
                out++ ; a[i].leave = time ;
                last[out&1] = a[i] ;
            }
        }
        sort( a , a + n ) ;
        for ( int i = 0 ; i < n - 1 ; i++ ) {
            if ( a[i].pos == a[i+1].pos ) 
                turn( i , i+1 ) ;
        }
        if ( out >= n ) break ;
    }
    int ans = last[out&1].id;
    if ( last[1].leave == last[0].leave ) {
        if ( last[0].dir[0] == 'L' )
            ans = last[0].id ;
        else 
            ans = last[1].id ;
    } 
    cout << time << ' ' << ans << endl ;
}
int main()
{
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);
    while ( scanf( "%d%d" ,&n ,&l ) , n || l ) {
        for ( int i = 0 ; i < n ; i++ ) {
            scanf( "%s%d" ,a[i].dir ,&a[i].pos ) ;
            a[i].id = i + 1 ;
        }
        sort( a , a + n ) ;
        work() ;
    }
    return 0;
}

C - Count the Regions

题意:

给 n 个矩形的坐标,计算整个平面被矩形分成了几个区域。

思路:

先离散化,然后把所有矩形边标记为1,然后枚举每一个图内的点,若标记为0则dfs染色,遇到边界停止,并且将答案+1.

代码:

//2015/11/1 13:55:53
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<functional>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<ctime>
using namespace std;
#define clr( x , y ) memset(x,y,sizeof(x))
#define cls( x ) memset(x,0,sizeof(x))
#define pr( x ) cout << #x << " = " << x << endl 
#define pri( x ) cout << #x << " = " << x << " " 
#define test( t ) int t ; cin >> t ; int kase = 1 ; while( t-- )
#define out( kase ) printf( "Case %d: " , kase++ )
#define sqr( x ) ( x * x )
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
typedef long long lint;
const double eps = 1e-8 ;
const int inf = 0x3f3f3f3f ;
const long long INF = 0x3f3f3f3f3f3f3f3fLL ;

struct Rec{
    int l , r , t , b ;
    void input() {
        scanf( "%d%d%d%d" ,&l ,&t ,&r ,&b ) ;
    }
}rec[60] ;
const int L = 1e6 + 10 ;
int x[120],y[120] ;
int xx[L] , yy[L] ;
int G[240][240] ;
int n , ans , kx , ky , lenx , leny ;
const int dir[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 } ;
void discrete()
{
    sort( x , x + kx ) ;
    sort( y , y + ky ) ;
    lenx = unique( x , x + kx ) - x ;
    leny = unique( y , y + ky ) - y ;
    for ( int i = 0 ; i < lenx ; i++ ) xx[x[i]] = 2 * i ;
    for ( int i = 0 ; i < leny ; i++ ) yy[y[i]] = 2 * i ;
    for ( int i = 0 ; i < n ; i++ ) {
        int x1 = xx[rec[i].l] ;
        int x2 = xx[rec[i].r] ;
        int y1 = yy[rec[i].t] ;
        int y2 = yy[rec[i].b] ;
        for ( int j = x1 ; j <= x2 ; j++ ) G[j][y1] = G[j][y2] = 1 ;
        for ( int j = y2 ; j <= y1 ; j++ ) G[x1][j] = G[x2][j] = 1 ;
    }
}

bool check( int i , int j )
{
    bool ok = ( i >= 1 && j >= 1 && i <= 2 * lenx - 2 && j <= 2 * leny - 2 ) ;
    return ok ;
}
void dfs( int i , int j ) 
{
    G[i][j] = 1 ;
    for ( int k = 0 ; k < 4 ; k++ ) {
        int tmpi = i + dir[k][0] ;
        int tmpj = j + dir[k][1] ;
        if ( check( tmpi , tmpj ) && !G[tmpi][tmpj] )
            dfs( tmpi , tmpj ) ;
    }
}
void find() {
    ans = 0 ;
    for ( int i = 0 ; i < ( lenx << 1 ) ; i++ ) {
        if ( !G[i][0] ) dfs( i , 0 ) ;
        int tmp = ( leny << 1 ) - 1 ;
        if ( !G[i][tmp] ) dfs( i , tmp ) ;
    }
    for ( int i = 0 ; i < ( leny << 1 ) ; i++ ) {
        if ( !G[0][i] ) dfs( 0 , i ) ;
        int tmp = ( lenx << 1 ) - 1 ;
        if ( !G[tmp][i] ) dfs( tmp , i ) ;
    }
    for ( int i = 0 ; i < ( lenx << 1 ) ; i++ ) {
        for ( int j = 0 ; j < ( leny << 1 ) ; j++ ) {
            if ( !G[i][j] ) ans++ , dfs( i , j ) ;
        }
    }
}
int main()
{
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);
    while ( cin >> n && n ) {
        kx = 0 , ky = 0 ;
        cls( G ) ;
        for ( int i = 0 ; i < n ; i++ ) {
            rec[i].input() ;
            x[kx++] = rec[i].l ;
            x[kx++] = rec[i].r ;
            y[ky++] = rec[i].t ;
            y[ky++] = rec[i].b ;
        }
        discrete() ;
        find() ;
        cout << ans + 1 << endl ;
    }
    return 0;
}

D - Clock Hands

题意:

有一个时钟,时针每 H 小时走一圈。
现在给你一个时刻,求该时刻之后最近的时刻 t ,在时刻 t ,秒针与分针的夹角等于秒针与时针的夹角。
时刻 t 的秒数用分数表示。

思路:

先吐槽一下,,时钟的题目一般都是在纸上写啊写,推出个公式,然后照着公式A过去就行了,结果这题用了半小时推公式,写代码用了两小时。。还是 too simple 啊。
设时钟一周的角度为 1,输入时刻为 h : m : s。

angs=s60angm=m60+s3600angh=hH+m60H+s3600H

设经过 t 秒后,秒针与分针的夹角等于秒针与时针的夹角,即
2angs=angm+angh

还有一种情况是分针在0右边,时针在0左边,起初秒针在0
左边,过了几秒后秒针过0,此时,相当于秒针角度减少了一周,所以要在原公式里补上圈数k(k是整数):
 angs=angs+angtangmangs=angs+1angh2angs+1=angm+angh 2(angt+angs)+k=angm+angh

至于分数的处理可以用分数类来做,或者把上式代入来计算gcd:

(119H1)(s+t)=60(H+1)m+3600h3600Hk

代码:

#include<bits/stdc++.h>
using namespace std;

int gcd( int a , int b ) {
    return b? gcd( b ,a % b ) : a ;
}
int main()
{
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);
    int H , h , m , s ;
    while ( cin >> H >> h >> m >> s ) {
        if ( !H && !m && !h && !s ) break ;
        int tmp = 119*H - 1 ;
        //(119H-1)(s+t) = 60(1+H)m + 3600h - 3600Hk 
        int t = 60*(1+H)*m + 3600*h - 3600*H - tmp*s ;
        while ( t < 0 ) t += 3600*H ;
        int sec = 60 * s * tmp + 60 * t ;
        int min = 60 * m * tmp + sec / 60 ;
        int hour ;
        if ( sec == min ) t += 3600*H ;
        sec = s * tmp + t ;
        min = m + sec / ( tmp * 60 ) ;
        hour = h + min / 60  ;
        min %= 60 ; hour %= H ; sec %= tmp * 60 ;
        int d = gcd( sec , tmp ) ;
        printf( "%d %d %d %d\n" ,hour ,min ,sec/d ,tmp/d ) ;
    }
    return 0;
}

E - Dragon’s Cruller

思路:

题意略去。。
这题想通了其实挺简单的,康拓展开+优先队列+bfs。
把九宫格转换成一行,那么一个状态就是一种排列,用康拓展开将排列映射到一个数字,然后搜索状态直到符合所求状态,用优先队列维护花费。

代码:

/*
Creat Time:2015/11/2 21:27:15
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<functional>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<ctime>
#define clr( x , y ) memset(x,y,sizeof(x))
#define cls( x ) memset(x,0,sizeof(x))
#define pr( x ) cout << #x << " = " << x << endl 
#define pri( x ) cout << #x << " = " << x << " " 
#define test( t ) int t ; cin >> t ; int kase = 1 ; while( t-- )
#define out( kase ) printf( "Case %d: " , kase++ )
#define sqr( x ) ( x * x )
#define mp make_pair
#define pii pair<int,int>
#define fi fist
#define se second
#define pb push_back
using namespace std;
typedef long long lint;
const double eps = 1e-8 ;
const int inf = 0x3f3f3f3f ;
const long long INF = 0x3f3f3f3f3f3f3f3fLL ;

const int fac[10] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 } ;
const int N = 363000 ;
int ch , cv ;
int a[9] , b[9] , cost[N] ;
int fi , ed ;
bool vis[N] ;
struct Node{
    int x , w ; 
    Node( int _x , int _w ):x(_x) , w(_w) {} 
    bool operator < ( const Node &a )const {
        if ( w != a.w ) return w > a.w ;
        return x < a.x ;
    }
} ;
priority_queue<Node> q ;
int A2int( int *a , int n )
{
    int res = 0 ;
    for ( int i = 0 ; i < n ; i++ ) {
        int tmp = a[i] ;
        for ( int j = 0 ; j < i ; j++ ) {
            if ( a[j] < a[i] ) tmp-- ;
        }
        res += fac[n-1-i] * tmp ;
    }
    return res ;
}
void int2A( int x , int *a , int n )
{
    bool flag[10] ;
    int j ;
    cls( flag ) ;
    for ( int i = 0 ; i < n ; i++ ) {
        int tmp = x / fac[n-1-i] ;
        for ( j = 0 ; j < n ; j++ ) {
            if ( !flag[j] ) {
                if ( !tmp ) break ;
                tmp -- ;
            }
        }
        a[i] = j , flag[j] = 1 ;
        x %= fac[n-i-1] ;
    }
}
void update( int num , int cos )
{
    if ( !vis[num] && cos < cost[num] )
        cost[num] = cos, q.push( Node( num , cos ) ) ;
}
void bfs() 
{
    while ( !q.empty() ) {
        Node p = q.top() ; q.pop() ;
        if ( vis[p.x] ) continue ;
        vis[p.x] = true ;
        if ( p.x == ed ) {
            printf( "%d\n" , p.w ) ;
            break ;
        }
        int2A( p.x , a , 9 ) ;
        int k = 0 , tmp ;
        while ( a[k] > 0 ) k++ ;

        swap( a[k] , a[(k+1) % 9] ) ; //left
        tmp = A2int( a , 9 ) ;
        update( tmp , p.w + ch ) ;
        swap( a[k] , a[(k+1) % 9] ) ; 

        swap( a[k] , a[(k+8) % 9] ) ; //right
        tmp = A2int( a , 9 ) ;
        update( tmp , p.w + ch ) ;
        swap( a[k] , a[(k+8) % 9] ) ;

        swap( a[k] , a[(k+3) % 9] ) ; //up
        tmp = A2int( a , 9 ) ;
        update( tmp , p.w + cv ) ;
        swap( a[k] , a[(k+3) % 9] ) ; 

        swap( a[k] , a[(k+6) % 9] ) ; //down
        tmp = A2int( a , 9 ) ;
        update( tmp , p.w + cv ) ;
        swap( a[k] , a[(k+6) % 9] ) ; 
    }
}
int main()
{
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);
    while ( ~scanf( "%d%d" ,&ch ,&cv ) ) {
        if ( !ch && !cv ) break ;
        for ( int i = 0 ; i < 9 ; i++ ) scanf( "%d" , a + i ) ;
        for ( int i = 0 ; i < 9 ; i++ ) scanf( "%d" , b + i ) ;
        fi = A2int( a , 9 ) ; ed = A2int( b , 9 ) ;
        cost[fi] = 0 ;
        while ( !q.empty() ) q.pop() ;
        cls( vis ) , clr( cost , inf ) ;
        q.push( Node( fi , 0 ) ) ;
        bfs() ;
    }
    return 0;
}

I - Hidden Tree

DP我只认狗哥!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值