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 ;
}