2014 Multi-University Training Contest 1小记

hdu  4862 网络流

给定n*m的矩阵,选<=k个起点 ,每个起点可以向右或向下跳任意步 ,花费是2点间的曼哈顿距离 。

 若2个格子的数字一样 , 则赚取格子上的数字的价值 , 遍历整个图的最小花费 。 每个点只经过一次。

若不能遍历则输出-1

最小K路径覆盖的模型,用费用流或者KM算法解决,构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,流量1,费用0,Y部有N*M个节点,每个节点向汇点连一条边,流量1,费用0,如果X部的节点x可以在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量,流量1,再在X部增加一个新的节点,表示可以从任意节点出发K次,源点向其连边,费用0,流量K,这个点向Y部每个点连边,费用0,流量1,最这个图跑最小费用最大流,如果满流就是存在解,反之不存在,最小费用的相反数就是可以获得的最大能量

const int maxn = 5000 ;
const int maxm = 50000 ;
const int inf = 1000000000 ;
struct Edge{
       int v , f , w , next ;
       Edge(){}
       Edge(int _v , int _f , int _w , int _next):v(_v),f(_f),w(_w),next(_next){}
};
int  g[maxn + 10] ;
Edge e[maxm + 10] ;
int  source , meet ;
int  id ;
int  maxflow ;

void  add(int u , int v  , int f , int w){
      e[++id] = Edge(v , f , w , g[u]) ;
      g[u] = id ;
      e[++id] = Edge(u , 0 , -w , g[v]) ;
      g[v] = id ;
}

queue<int> que ;
bool in[maxn + 10] ;
int  dist[maxn + 10] ;
int  pv[maxn + 10] , pe[maxn + 10] ;

int bfs(){
     while(! que.empty()) que.pop() ;
     que.push(source) ;
     memset(dist , 63 , sizeof(dist)) ;
     dist[source] = 0  ;
     in[source] = 1 ;
     while(! que.empty()){
           int u = que.front() ; que.pop() ;
           in[u] = 0 ;
           for(int i = g[u] ; i ; i = e[i].next){
                int  v = e[i].v ;
                if(e[i].f > 0 && dist[u] + e[i].w < dist[v]){
                      dist[v] = dist[u] + e[i].w ;
                      pv[v] = u ;
                      pe[v] = i ;
                      if(! in[v]){
                            in[v] = 1 ;  que.push(v) ;
                      }
                }
           }
     }
     return  dist[meet] < inf  ;
}

int  augment(){
     int u = meet  ;
     int delta = inf ;
     while(u != source){
           delta = min(delta , e[pe[u]].f) ;
           u = pv[u] ;
     }
     u = meet ;
     while(u != source){
           e[pe[u]].f -= delta ;
           e[pe[u] ^ 1].f += delta ;
           u = pv[u] ;
     }
     maxflow += delta ;
     return dist[meet] * delta ;
}

int  mincostflow(){
     int ans = 0 ;
     while(bfs())  ans += augment() ;
     return ans ;
}

void init(){
     memset(g , 0 , sizeof(g)) ;
     id = 1 ;
}

int   n ,  m  ;
int   lable(int i , int j , int k){
      return n*m*k + (i-1) * m + j ;
}

int   main(){
      int  i , j ,  k , u , v ,  w , T = 1  , t ;
      char  str[12][12] ;
      cin>>t ;
      while(t--){
           scanf("%d%d%d" , &n , &m , &k)  ;
           for(i = 1 ; i <= n ; i++) scanf("%s" , str[i] + 1) ;
           init() ;
           source = 2*n*m + 1 ;
           meet = 2*n*m + 2 ;
           for(i = 1 ; i <= n ; i++){
               for(j = 1 ;  j <= m ; j++){
                    u = lable(i , j , 0) ;
                    add(source , u , 1 , 0) ;
                    for(int d = j+1 ; d <= m ; d++){
                         v = lable(i , d , 1) ;
                         w = d - j - 1 ;
                         if(str[i][j] == str[i][d]) w -= str[i][j] - '0' ;
                         add(u , v , 1 , w) ;
                    }
                    for(int d = i+1 ; d <= n ; d++){
                         v = lable(d , j , 1) ;
                         w = d - i - 1 ;
                         if(str[i][j] == str[d][j]) w -= str[i][j] - '0' ;
                         add(u , v , 1 , w) ;
                    }
                    u = lable(i , j , 1) ;
                    add(u , meet , 1 , 0) ;
               }
           }
           u = 2*n*m + 3 ;
           add(source , u , k , 0) ;
           for(i = 1 ; i <= n ; i++){
               for(j = 1 ; j <= m ; j++){
                   v = lable(i , j , 1) ;
                   add(u , v , 1 , 0) ;
               }
           }
           maxflow = 0 ;
           w = mincostflow() ;
           if(maxflow == n*m)  printf("Case %d : %d\n" , T++ , -w) ;
           else                printf("Case %d : -1\n" , T++) ;

      }
      return 0 ;
}


hdu  4865  dp 

dp[i][j]  : 第i天,天气为j的最大概率。 那么第i天的天气为 dp[i][0] , dp[i][1] , dp[i][2] 中最大值所对应的{0 , 1 , 2} 

dp[i][j]  =  max{ dp[i-1][k] * yetd[k][j] * table[j][leave[i]] }  k = 0 , 1 , 2 

因为我们只知道第i天的leave值。 所以dp[i][j] 实际上表示的第i天,天气为j的最大概率 是不准确的, 

说的准确点应该是 一个估价函数值 。

当第i天的天气确定,便知道通过转移过来的第i-1天的天气k。

const int maxn = 58 ;
double  dp[maxn][3] ;
double  table[3][4] = {{0.6 , 0.2 , 0.15 , 0.05} ,
                       {0.25 , 0.3 , 0.2 , 0.25} ,
                       {0.05 , 0.10 , 0.35 , 0.50}};

double  yetd[3][3] = {{0.5 , 0.375 , 0.125 } ,
                      {0.25 , 0.125 ,0.625} ,
                      {0.25 , 0.375 , 0.375}} ;

int   leave[maxn] ;
int   father[maxn][3] ;
int   ans[maxn] ;

int   main(){
      int t  , n  , i  , j , k  , T = 1  ;
      char  str[8] ;
      cin>>t  ;
      while(t--){
           cin>>n ;
           for(i = 1 ; i <= n ; i++){
               scanf("%s" , str) ;
               if(strcmp(str , "Dry") == 0) leave[i] = 0 ;
               else if(strcmp(str , "Dryish") == 0) leave[i] = 1 ;
               else if(strcmp(str , "Damp") == 0) leave[i] = 2 ;
               else leave[i] = 3 ;
           }
           dp[1][0] = 0.63 * table[0][leave[1]] ;
           dp[1][1] = 0.17 * table[1][leave[1]] ;
           dp[1][2] = 0.20 * table[2][leave[1]] ;
           for(i = 2 ; i <= n ; i++){
               for(j = 0 ; j < 3 ; j++){
                    dp[i][j] = -1.0 ;
                    for(k = 0 ; k < 3 ; k++){
                        double w  =  dp[i-1][k] * yetd[k][j] * table[j][leave[i]] ;
                        if(w > dp[i][j]){
                              dp[i][j] = w ;
                              father[i][j] = k ;
                        }
                    }
               }
           }
           double mx = -1.0 ;
           for(i = 0 ;  i < 3 ; i++){
               if(dp[n][i] > mx){
                    mx = dp[n][i] ;
                    ans[n] = i ;
               }
           }
           k = ans[n] ;
           for(i = n-1 ; i >= 1 ; i--){
               k = father[i+1][k] ;
               ans[i] = k ;
           }
           printf("Case #%d:\n" , T++) ;
           for(i = 1 ; i <= n ; i++){
               if(ans[i] == 0) puts("Sunny") ;
               else if(ans[i] == 1) puts("Cloudy") ;
               else  puts("Rainy")  ;
           }
      }
      return  0 ;
}



hdu 4869  数学

 

题意:输入操作次数n和扑克牌数m,一开始扑克牌全都背面朝上。现在输入n个数xi,表示选择xi张牌翻转,问最后的牌的情况有多少种可能?

最后的答案就是ans=sigma C(m,k),k为所有能取到的1的个数。

最后1的个数的奇偶性,跟所有翻牌数的和的奇偶相同(每次翻牌,要么0->1,要么1->0,都是在改变1的个数奇偶)。

之后我们需要找到最少有mi个1,以及最大有mx个1;

费马小定理,假如p是质数,且gcd(a,p)=1,

那么 a^(p-1) ≡1(mod p)。

=>a^(p-2)=p^(-1)(mod p)

既有

(a/b) %M=a*(b^(M-2))%M 

typedef long long  LL ;
const  LL  mod = 1000000009LL ;
LL    Pow(LL x , LL y){
      LL s = 1 ;
      for(; y ; y >>= 1){
          if(y & 1){ s *= x ; s %= mod ;}
          x *= x ; x %= mod ;
      }
      return s ;
}

const  int  maxn = 100008 ;
LL    c[maxn]  ;//c[i] = C(m , i)

int   main(){
      int x , i , j , L , R , mi , mx  , n , m ;
      while(scanf("%d%d" , &n , &m) != EOF){
           mi = mx = 0 ;
           for(i = 1 ; i <= n ; i++){
                scanf("%d" , &x) ;
                if(mx + x <= m)  R = mx + x ;
                else if(mi + x <= m){
                     if((mi + x)%2 == m%2)  R = m ;
                     else  R = m-1 ;
                }
                else  R = m - (x - (m - mi)) ;
                if(mi - x >= 0)  L = mi - x ;
                else if(mx - x >= 0){
                     if(mi%2 == x%2) L = 0 ;
                     else  L = 1 ;
                }
                else  L = x - mx ;
                mi = L , mx = R ;
           }
           c[0] = 1LL ;
           for(LL i = 1 ; i <= m ; i++){
                if(m - i < i) c[i] = c[m-i] ;
                else c[i] = c[i-1] * (LL)(m-i+1) % mod * Pow(i , mod-2) % mod ;
           }
           LL  sum = 0 ;
           for(int i = mi ; i <= mx ; i += 2){
                sum += c[i] ;
                sum %= mod ;
           }
           cout<< sum << endl ;
      }
      return 0 ;
}


hdu 4870  高斯消元

一个人注册两个账号,初始rating都是0,他每次拿低分的那个号去打比赛,赢了加50分,输了扣100分,胜率为p,他会打到直到一个号有1000分为止,问比赛场次的期望

+50  => +1 ;

-100 => -2 ;

思路:f(i, j)表示i >= j,第一个号i分,第二个号j分时候,达到目标的期望,那么可以列出转移为f(i, j) = p f(i', j') + (1 - p)f(i'' + j'') + 1
f(i', j')对应的是赢了加分的状态,f(i'', j'')对应输的扣分的状态,可以把50分当作一个单位,一共有20 * 21 / 2 = 210个状态,也就是对应了210个方程组,利用高斯消元去求解方程组,解出f(0, 0)就是答案

dp[i][j]   (i >=j )  

状态情况 

dp[0][0]                                                  1

dp[1][0] , dp[1][1] ,                                 2

.....

dp[19][0] , dp[19][1] ,  ,,,, dp[19][19]       20 

all  =  1 + 2 + 3 + ... + 20 =  (1 + 20) * 20 / 2 = 210 个。

做个hash映射,

dp[0][0]  = > x1

dp[1][0] = > x2  , dp[1][1] = > x3 ,

....

dp[19][19]  => x210  。

构造 方程组即可

a11 x1 + a12 x2 + ......a1n xn =  y1

a21 x1 + a22 x2 + ......a2n xn =  y2

.....

an1 x1 + an2 x2 + ......ann xn =  yn


//------begin of guass()-------------------/
const   int   maxn = 220 ;
const   double  eps = 1e-9 ;

double  a[maxn][maxn] , x[maxn] ;  //a[i][j] 系数矩阵 , a[i][n+1]  = y[i] , x解
int     n ;   //n个方程
int     l[maxn] ; // 自由元

void   guass(){
       memset(l , 0 , sizeof(l)) ;
       int r = 1 ,  res = 0 ;
       for(int i = 1 ; i <= n ; i++){
           for(int j = r ; j <= n ; j++){
                if(fabs(a[j][i]) > eps){
                     for(int k = i ; k <= n+1 ; k++)
                         swap(a[j][k] , a[r][k]) ;
                     break ;
                }
           }
           if(fabs(a[r][i]) < eps){
                 res++  ;
                 continue ;
           }
           for(int j = 1 ; j <= n ; j++){
                if(j != r && fabs(a[j][i]) > eps){
                      double  temp = a[j][i] / a[r][i] ;
                      for(int k = i ; k <= n+1 ; k++)
                          a[j][k] -= temp * a[r][k] ;
                }
           }
           l[i] = 1 ;
           ++r ;
       }
       for(int i = 1 ; i <= n ; i++){
           if(l[i]){
              for(int j = 1 ; j <= n ; j++){
                 if(fabs(a[j][i]) > 0)
                      x[i] = a[j][n+1] / a[j][i] ;
              }
           }
       }
}

//------end of guass()-------------------/

int    lable[25][25] ;

int    main(){
       memset(lable , -1 , sizeof(lable)) ;
       int i , j  ,  t = 0  , u , v  ;
       for(i = 0 ; i < 20 ; i++){
           for(j = 0 ; j <= i ; j++)  lable[i][j] = ++t ;
       }
       n =  210 ;
       double  p  ;
       while(cin>>p){
             memset(a , 0  , sizeof(a)) ;
             for(i = 0 ; i < 20 ; i++){
                  for(j = 0 ; j < i ; j++){
                        u = lable[i][j] ;
                        a[u][u] = 1.0 ;
                        a[u][n+1] = 1.0 ;
                        v = lable[i][j+1] ;
                        a[u][v] -= p ;
                        v = lable[i][max(0 , j-2)] ;
                        a[u][v] -= (1.0-p) ;
                  }
                  u = lable[i][i] ;
                  a[u][u] = 1.0 ;
                  a[u][n+1] = 1.0 ;
                  v = lable[i+1][i] ;
                  a[u][v] -= p ;
                  v = lable[i][max(0 , i-2)] ;
                  a[u][v] -= (1.0-p) ;
             }
             guass() ;
             printf("%.6lf\n" , x[1]) ;
       }
       return  0 ;
}


 






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值