几个DP
来源:互联网 发布:软件开发项目管理流程 编辑:程序博客网 时间:2024/05/16 06:14
Xxx正在玩一款游戏,游戏地图上有N个点,这些点之间有M条边。游戏系统会在一定时间在某点刷新出一定量的怪,系统刷新出来的怪只会存在1秒,下一秒就会消失。如果那个时间xxx正好在那个点,那么xxx可以秒杀这个点上的所有怪。另外,xxx还有B次放大招的机会,每次放大招可以秒杀掉当前所在点及与其相邻点上的所有怪。大招有5秒的冷却时间,也就是说每次放大后要经过5秒钟才能再次放大。Xxx想知道T时间内他最多可以杀掉多少只怪。Xxx可以从任意点开始。系统时间从1开始。
输入的第一行包含一个整数CT(CT<=50), 表示一共有CT组测试数据。
对于每组测试数据,第一行包含5个整数N、M、T、K、B(1 <= N, T <= 50, 0<=M<= (N-1)*N/2, 0<=K<=10000, 0<=B<=5)。N、M、T意义如上所述,K表示有K个系统刷新。
接下来是M行,每行有3个整数u、v、t(1<=u, v<=N, u!=v, 1<=t<=10)表示从u走到v或者从v走到u需要花费t的时间。数据保证没有重边。
然后是K行,每行有3个整数t、p、c(1<=t<=50, 1<=p<=N, 1<=c<=100)表示t时间 在p点刷新出c个怪。
Output
对于每组测试数据,输出在T时间内xxx最多可以杀掉多少只怪
const int maxn = 58 ;int cnt[maxn][maxn] ;int dist[maxn][maxn] ;int sum[maxn][maxn] ;int dp[maxn][maxn][6][6] ;void init(){ memset(cnt , 0 , sizeof(cnt)) ; memset(dist , -1 , sizeof(dist)) ; memset(sum , 0 , sizeof(sum)) ; memset(dp , -1 , sizeof(dp)) ;}int checkmax(int &a , int b){ if(a < b) a = b ;}int N , M , T , B ;int DP(){ int ans = 0 ; for(int i = 1 ; i <= N ; i++) dp[0][i][0][5] = 0 ; for(int ti = 0 ; ti <= T ; ti++){ for(int pi = 1 ; pi <= N ; pi++){ for(int bi = 0 ; bi <= B ; bi++){ for(int li = 0 ; li <= 5 ; li++){ if(dp[ti][pi][bi][li] == -1) continue ; checkmax(ans , dp[ti][pi][bi][li]) ; for(int np = 1 ; np <= N ; np++){ if(dist[pi][np] != -1 && ti + dist[pi][np] <= T){ int nt = ti + dist[pi][np] ; int nl = li + dist[pi][np] ; if(nl > 5) nl = 5 ; checkmax(dp[nt][np][bi][nl] , dp[ti][pi][bi][li] + cnt[np][nt]) ; if(nl == 5 && bi != B) checkmax(dp[nt][np][bi+1][0] , dp[ti][pi][bi][li] + sum[np][nt]) ; } } } } } } return ans ;}int main(){ int cas , i , j , u , v , t , k , p , c ; cin>>cas ; while(cas--){ scanf("%d%d%d%d%d" ,&N,&M,&T,&k,&B) ; init() ; for(i = 1 ; i <= M ; i++){ scanf("%d%d%d" ,&u,&v,&t) ; dist[u][v] = dist[v][u] = t ; } for(i = 1 ; i <= N ; i++) dist[i][i] = 1 ; while(k--){ scanf("%d%d%d" ,&t ,&p ,&c) ; cnt[p][t] += c ; } for(i = 1 ; i <= N ; i++){ for(j = 1 ; j <= N ; j++){ if(dist[i][j] != -1){ for(k = 0 ; k <= T ; k++) sum[i][k] += cnt[j][k] ; } } } printf("%d\n" ,DP()) ; } return 0 ;}
给n个数,从这n个数中选择i个数,共有c(n , i)种情况,将每种情况中的i个数异或,将这c(n , i)个异或结果求和,就得到第i个输出结果,i属于[1 n]。
求x个数的异或,等于分别对x个数的同一二进制位进行异或,然后加权求和。于是将n个数表示成二进制的形式,对于本题,32位就够。因为,奇数个1的异或 = 1 , 偶数个1的异或 = 0 。 统计每位上1的个数 ,然后对于第j个二进制位,枚举所选中的1的个数,加权求和,即可得结果。将对n个数的处理,转化成对32个位的处理。
typedef long long LL ;const int mod = 1000003 ;int bit[32] ;LL C[1001][1001] ;LL ans[1001] ;void getC(){ C[0][0] = C[1][0] = C[1][1] = 1 ; for(int i = 2 ; i <= 1000 ; i++){ C[i][0] = C[i][i] = 1 ; for(int j = 1 ; j < i ; j++){ C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod ; } }}int main(){ getC() ; int n , x , i , j , k ; while(cin>>n){ memset(bit , 0 , sizeof(bit)) ; memset(ans , 0 , sizeof(ans)) ; for(i = 1 ; i <= n ; i++){ scanf("%d" , &x) ; for(j = 0 ; j < 32 ; j++){ if(x & (1<<j)) bit[j]++ ; } } for(k = 1 ; k <= n ; k++){ for(i = 0 ; i < 32 ; i++){ for(j = 1 ; j <= bit[i] && j <= k ; j += 2){ ans[k] += ((C[bit[i]][j] * C[n-bit[i]][k-j] % mod )* ( (1<<i) % mod )); ans[k] %= mod ; } } } printf("%I64d" , ans[1]) ; for(i = 2 ; i <= n ; i++) printf(" %I64d" , ans[i]) ; puts("") ; } return 0 ;}
给定一个大小为n的数组,数组的元素a[i]代表第i天的股票价格。计算在最多允许买卖k次(一买一卖记为一次)的条件下的最大收益。
需要注意的是,你不能同时拥有两份股票。也就是说在下次买入前,你必须把手头上原有的股票先卖掉。
解题思路:
令dp[i][j]表示前i天买卖j次可以获得的最大收入。对于第i天,如果不卖则dp[i][j] = dp[i-1][j];
dp[i][j] = dp[k][j-1] + value[i] - value[k]。
dp[i][j] = max{ dp[i-1][j] ,dp[k][j-1] + value[i] - value[k] },0<k<i;这个地方可以直接优化
const int maxn = 1008 ;int dp[maxn][maxn] ;int x[maxn] , n , k ;int DP(){ int i , j , mx ; memset(dp , 0 , sizeof(dp)) ; for(j = 1 ; j <= k ; j++){ mx = dp[1][j-1] - x[1] ; for(i = 1 ; i <= n ; i++){ dp[i][j] = max(dp[i-1][j] , x[i] + mx) ; mx = max(mx , dp[i][j-1] - x[i]) ; } } return dp[n][k] ;}
密码锁,给初始状态和目标状态。每次可正向或者反向旋转连续的 1 , 2 , 3 位。数字在0 ---9 之间循环,即9+1 -> 0, 0-1 -> 9。问从初始状态到目标状态的最少旋转次数。
dp[ i ][j ][ k ]表示[0 ,i-1]位已经匹配了,第i 的数字是 j ,第 i +1 目前的数字是k ,到目标状态需要的最少旋转次数。显然dp[ len ][ 0 ] [ 0 ] 中的最小值为要求的值,其中j 在[ 0 ,9] ,k在 [ 0 , 9];
int up[10][10] , down[10][10] ;int UP(int u , int v){ int s = 0 ; while(u != v){ s++ ; u++ ; if(u == 10) u = 0 ; } return s ;}int DOWN(int u , int v){ int s = 0 ; while(u != v){ s++ ; u-- ; if(u == -1) u = 9 ; } return s ;}void get(){ for(int i = 0 ; i <= 9 ; i++){ for(int j = 0 ; j <= 9 ; j++){ up[i][j] = UP(i , j) ; down[i][j] = DOWN(i , j) ; } }}void checkmin(int &a , int b){ if(a > b) a = b ;}const int maxn = 1008 ;char S[maxn] , T[maxn] ;int dp[maxn][10][10] ;int DP(){ int n = strlen(S) , i , j , k , t , jj , kk ; S[n] = S[n+1] = T[n] = T[n+1] = '0' ; memset(dp , 63 , sizeof(dp)) ; dp[0][S[0]-'0'][S[1]-'0'] = 0 ; for(i = 1 ; i <= n ; i++){ for(j = 0 ; j <= 9 ; j++){ for(k = 0 ; k <= 9 ; k++){ t = up[j][T[i-1] - '0'] ; for(jj = 0 ; jj <= t ; jj++){ for(kk = 0 ; kk <= jj ; kk++) checkmin(dp[i][(k+jj)%10][(S[i+1]-'0'+kk)%10] , dp[i-1][j][k] + t) ; } t = down[j][T[i-1] - '0'] ; for(jj = 0 ; jj <= t ; jj++){ for(kk = 0 ; kk <= jj ; kk++) checkmin(dp[i][(k-jj+10)%10][(S[i+1]-'0'+10-kk)%10] , dp[i-1][j][k] + t) ; } } } } return dp[n][0][0] ;}int main(){ get() ; int i , j ; while(scanf("%s%s" , S ,T) != EOF){ printf("%d\n" ,DP()) ; } return 0 ;}
子序列的定义:对于一个序列a=a[1],a[2],......a[n]。则非空序列a'=a[p1],a[p2]......a[pm]为a的一个子序列,其中1<=p1<p2<.....<pm<=n。
例如4,14,2,3和14,1,2,3都为4,13,14,1,2,3的子序列。
对于给出序列a,请输出不同的子序列的个数。
memset(pos , -1 , sizeof(pos)) ; dp[0] = 0 ; for(i = 1 ; i <= n ; i++){ x = getint() ; if(pos[x] == -1) dp[i] = (2*dp[i-1] + 1) % mod ; else dp[i] = (2*dp[i-1] - dp[pos[x]-1] + mod) % mod ; pos[x] = i ; } cout<<dp[n]<<endl ;
求i在[a,b]与j在[c,d]之间, xor值大于e的 sum += i ^ j;
typedef long long LL ;typedef pair<LL , LL> pLL ;const int maxn = 65 ;const LL mod = 1000000007 ;int bita[maxn] , bitb[maxn] , bitc[maxn] ;void getbit(int dig[] , LL x , int &len){ len = 0 ; while(x){ dig[len++] = x%2 ; x /= 2 ; }}pLL dp[maxn][2][2][2] ;pLL DP(int pos , int enda , int endb , int endc){ if(pos < 0){ return endc ? make_pair(0 , 0) : make_pair(1 , 0) ; } if(dp[pos][enda][endb][endc].first != -1 && dp[pos][enda][endb][endc].second != -1) return dp[pos][enda][endb][endc] ; int da = enda ? bita[pos] : 1 ; int db = endb ? bitb[pos] : 1 ; int dc = endc ? bitc[pos] : -1 ; pLL s = make_pair(0 , 0) ; for(int i = 0 ; i <= da ; i++){ for(int j = 0 ; j <= db ; j++){ int t = i ^ j ; if(t >= dc){ pLL k = DP(pos-1 , enda&&i==da , endb&&j==db , endc&&t==dc) ; s.first = (s.first + k.first) % mod ; s.second = ((s.second + k.second) % mod) + (1LL<<pos)*t%mod*k.first%mod ; s.second %= mod ; } } } return dp[pos][enda][endb][endc] = s ;}LL answer(LL a , LL b , LL c){ for(int i = 0 ; i < maxn ; i++) for(int j = 0 ; j <= 1 ; j++) for(int k = 0 ; k <= 1 ; k++) for(int p = 0 ; p <= 1 ; p++) dp[i][j][k][p] = make_pair(-1 , -1) ; int la , lb , lc , len ; getbit(bita , a , la) ; getbit(bitb , b , lb) ; getbit(bitc , c , lc) ; len = max(la , max(lb , lc)) ; while(la < len) bita[la++] = 0 ; while(lb < len) bitb[lb++] = 0 ; while(lc < len) bitc[lc++] = 0 ; return DP(len-1 , 1 , 1 , 1).second ;}int main(){ int t , i , T = 1 ; LL a , b, c , d , e , s ; cin>>t ; while(t--){ cin>>a>>b>>c>>d>>e ; s = ((answer(b,d,e) - answer(a-1,d,e) - answer(b,c-1,e) + answer(a-1 , c-1,e)) %mod + mod) % mod ; printf("Case %d: %I64d\n" , T++ , s) ; } return 0 ;}
题目大意:给一定区间[A,B],一串由/,\,-组成的符号串。求满足符号串的数字个数。
/表示数字从左到右递增
\表示数字从左到右递减
-表示数字从左到右相等
例如:
/-\ 可以表示
1221,123455543
数据规模:
A,B<=10^100
const int maxn = 101 ;const int mod = 100000000 ;char str[maxn] ;int Len ;int bit[maxn] ;int dp[maxn][maxn][10] ;inline int ok(int i , int u , int v){ if(str[i] == '/') return u < v ; if(str[i] == '-') return u == v ; if(str[i] == '\\') return u > v ;}int DP(int pos , int id , int pre , int islead , int isend){ if(pos == -1) return id == Len ; if(!isend && dp[pos][id][pre] != -1) return dp[pos][id][pre] ; int s = 0 ; int d = isend ? bit[pos] : 9 ; for(int i = 0 ; i <= d ; i++){ if(islead) s += DP(pos-1 , id , i , islead&&i==0 , isend&&i==d) ; else if(id < Len && ok(id , pre , i)) s += DP(pos-1 , id+1 , i , islead , isend&&i==d) ; else if(id > 0 && ok(id-1 , pre , i)) s += DP(pos-1 , id , i , islead , isend&&i==d) ; s %= mod ; } if(! isend) dp[pos][id][pre] = s ; return s ;}int answer(char s[] , int d){ int i = 0 , j , ls = strlen(s) , len = 0 ; while(s[i] == '0') i++ ; for(j = ls-1 ; j >= i ; j--) bit[len++] = s[j] - '0' ; if(d&&len>0){ for(i = 0 ; i < len ; i++){ if(bit[i]){ bit[i]-- ; break ; } else bit[i] = 9 ; } } return DP(len-1 , 0 , 0 , 1 , 1) ;}int main(){ char a[maxn] , b[maxn] ; while(scanf("%s" , str) != EOF){ Len = strlen(str) ; scanf("%s%s" , a , b) ; memset(dp , -1 , sizeof(dp)) ; printf("%08d\n" , (answer(b , 0) - answer(a , 1) + mod) % mod ) ; } return 0 ;}
问从L到R有长度为K的严格最长上升子序列的数字的个数
dp[i][j][k]:严格最长上升子序列的数目。
i:当前进行到数字的位数,最低位为0。
j: 状态压缩,10个数字中出现过哪些数字。
k:当前最长上升子序列的长度。
LL dp[20][1025][11] ;int k ;int bit[20] ;int GetLen(int state){ int s = 0 ; while(state){ if(state & 1) s++ ; state >>= 1 ; } return s ;}int update(int x , int state){ for(int i = x ; i <= 9 ; i++){ if(state & (1<<i)) return (state - (1<<i)) | (1<<x) ; } return state | (1<<x) ;}LL DP(int pos , int state , int islead , int isend){ if(pos == -1) return GetLen(state) == k ; if(!isend && dp[pos][state][k] != -1) return dp[pos][state][k] ; LL s = 0 ; int d = isend ? bit[pos] : 9 ; for(int i = 0 ; i <= d ; i++){ s += DP(pos-1 , islead&&i==0 ? 0 : update(i , state) , islead&&i==0 , isend&&i==d) ; } if(! isend) dp[pos][state][k] = s ; return s ;}LL answer(LL x){ int len = 0 ; while(x){ bit[len++] = x % 10 ; x /= 10 ; } return DP(len-1 , 0 , 1 , 1) ;}int main(){ memset(dp , -1 , sizeof(dp)) ; LL l , r ; int t , T = 1 ; cin>>t ; while(t--){ cin>>l>>r>>k ; printf("Case #%d: " , T++) ; cout<< answer(r) - answer(l-1) << endl ; } return 0 ;}
const int maxn = 3001 ;int s[maxn] , dp[maxn][maxn] ;int dfs(int l , int r){ if(l > r) return dp[l][r] = 0 ; if(dp[l][r] != -1) return dp[l][r] ; dp[l][r] = max(dfs(l+1 ,r) , dfs(l , r-1)) ; if(s[l] == s[r]) dp[l][r] = max(dp[l][r] , dfs(l+1,r-1)+1) ; return dp[l][r] ;}int main(){ int i , ans , n ; while(cin>>n && n){ for(i = 1 ; i <= n ; i++){ scanf("%d" ,&s[i]) ; s[n+i] = s[n+n+i] = s[i] ; } memset(dp , -1 , sizeof(dp)) ; ans = 0 ; for(int i = 1 ; i <= n ; i++) ans = max(ans , dfs(i , i+2*n-1)) ; printf("%d\n" , ans) ; } return 0 ;}
问从L到R有回文数字的个数
ll dp[20][20][2];int bit[20],num[20];ll dfs(int pos,int m,int s,bool f){ if(!pos) return 1; if(!f&&dp[pos][m][s]!=-1) return dp[pos][m][s]; ll ans=0; int e=f?bit[pos]:9; for(int i=0;i<=e;i++){ if(!s){ num[pos]=i; ans+=dfs(pos-1,m-!i,s||i,f&&i==e); } else{ int t=(m+1)>>1; bool flag=m&1?pos<t:pos<=t; if(flag){ if(num[m+1-pos]==i) ans+=dfs(pos-1,m,s,f&&i==e); } else{ num[pos]=i; ans+=dfs(pos-1,m,s,f&&i==e); } } } if(!f) dp[pos][m][s]=ans; return ans;}ll cal(ll n){ int m=0; while(n){ bit[++m]=n%10; n/=10; } return dfs(m,m,0,1);}int main(){ int t,ca=0; ll a,b; memset(dp,-1,sizeof(dp)); scanf("%d",&t); while(t--){ scanf("%lld%lld",&a,&b); if(a>b) swap(a,b); printf("Case %d: %lld\n",++ca,cal(b)-cal(a-1)); } return 0;}
1:在区间[x,y]中b进制数,各数位和为m的数的个数
2:在区间[x,y]中b进制数,第k个各数位和为m的数是谁。
int dp[32][308] ;int b , bit[32] , m ;int DP(int pos , int s , int isend){ if(pos == -1) return s == m ; if(!isend && dp[pos][s] != -1) return dp[pos][s] ; int sum = 0 ; int d = isend ? bit[pos] : (b-1) ; for(int i = 0 ; i <= d ; i++){ sum += DP(pos-1 , s+i , isend&&i==d) ; } if(! isend) dp[pos][s] = sum ; return sum ;}int answer(int x){ int len = 0 ; while(x){ bit[len++] = x % b ; x /= b ; } return DP(len-1 , 0 , 1) ;}int main(){ int kind , l , r , k , T = 1 ; while(scanf("%d" , &kind) != EOF){ memset(dp , -1 , sizeof(dp)) ; if(kind == 1){ scanf("%d%d%d%d" ,&l ,&r , &b ,&m) ; printf("Case %d:\n" , T++) ; if(l > r) swap(l , r) ; printf("%d\n" , answer(r) - answer(l-1)) ; } else{ scanf("%d%d%d%d%d" ,&l ,&r , &b ,&m , &k) ; printf("Case %d:\n" , T++) ; if(l > r) swap(l , r) ; int low = answer(l-1) ; int high = answer(r) ; if(high - low < k){ puts("Could not find the Number!") ; continue ; } int L = l , R = r , M , s ; while(L <= R){ M = int(((LL)L + (LL)R)>>1) ; if(answer(M)-low >= k) s = M , R = M -1 ; else L = M + 1 ; } printf("%d\n" , s) ; } } return 0 ;}
- 几个DP
- 插头dp的几个模板
- 插头dp的几个模板
- 几个dp的小总结
- 几个相似的DP题
- 按位dp的几个经典例题
- 几个按位DP的总结
- 几个常见的DP问题及解法
- 几个常见的DP问题及解法
- PAT 1040 有几个PAT(dp)
- 终于把dp算法几个经典题目ac了
- Android 的几个单位dp 、sp、px的转换
- POJ 1159 最少添加几个字母构成回文 DP
- android ui的几个概念:px,dip(dp),sp,dpi,分辨率等
- android ui的几个概念:px,dip(dp),sp,dpi,分辨率等
- 几个基础数位DP (hdu 2089,hdu 3555 ,uestc 1307 windy 数)
- android ui的几个概念:px,dip(dp),sp,dpi,分辨率等
- 树dp (至少砍掉几个树枝,能得到有m个结点的子树)
- Java内存溢出的详细解决方案
- Java获取当前时间,并转化为String类型
- 自从那件事以后,这本画册就一直陪在我身边
- 曹丽丽:与百度互动必听--百度站长平台新动向
- javascript 用函数语句和表达式定义函数的区别
- 几个DP
- 纯CSS画的基本图形(矩形、圆形、三角形、多边形、爱心、八卦等),NB么
- C++ GUI QT 第4版 之线程(二) 线程的同步
- 双指针不带头结点的链栈的实现(C语言)
- Instruments: Heap Allocations and Anonymous VM
- linux内核hello world模块编写
- VBA编程时ListView Statusbar控件无法使用的奇怪问题
- 如何让div水平居中以及垂直居中,在宽高不定的情况下
- mssql数据库游标批量修改符合条件的记录