hdu 4862 网络流
给定n*m的矩阵,选<=k个起点 ,每个起点可以向右或向下跳任意步 ,花费是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 ; }