#include<iostream>#include<cstdio>#include<cstring>usingnamespace std;constexprint MAXN =(1<<10)+5;int n, nn, m;int a[105][15], f[MAXN];intmain(void){scanf("%d%d",&n,&m);
nn =1<< n;for(int i =1; i <= m;++i)for(int j =1; j <= n;++j)scanf("%d",&a[i][j]);memset(f,0x3f,sizeof(f));
f[nn -1]=0;for(int i = nn -1; i >=0;--i)for(int j =1; j <= m;++j){int now = i;for(int k =0; k < n;++k)if(a[j][k +1]==1) now &=(~(1<< k));elseif(a[j][k +1]==-1) now |=(1<< k);
f[now]=min(f[now], f[i]+1);}if(f[0]==0x3f3f3f3f)puts("-1");elseprintf("%d\n", f[0]);return0;}
#include<iostream>#include<cstdio>usingnamespace std;constint MOD =100000000;int n, m;int a[15][15];int f[15][4100];boolcheck(int row,int x){if(x &(x <<1))returnfalse;for(int i =0; i < m;++i)if(((x >> i)&1)==1&&!a[row][i])returnfalse;returntrue;}intmain(void){scanf("%d%d",&n,&m);for(int i =1; i <= n;++i)for(int j =0; j < m;++j)scanf("%d",&a[i][j]);
f[0][0]=1;for(int i =1; i <= n;++i){for(int j =0; j <(1<< m);++j)if(check(i, j))for(int k =0; k <(1<< m);++k)if((k & j)==0) f[i][j]=(f[i][j]+ f[i -1][k])% MOD;}int ans =0;for(int i =0; i <(1<< m);++i)
ans =(ans + f[n][i])% MOD;printf("%d\n", ans);return0;}
设 f(i,j) 代表终点为 j,走过的点状压后为 i 的方案数。转移呢?不太对,这样不知道当前位置。换句话说,状态定义的不够好,导致无法转移。
下面要求 i 的二进制位下第一个 1 代表当前走到的位置。这是什么?lowbit!转移的时候要求添加的点只能在 lowbit 后面。这样就很好写了。这种考虑按照顺序的转移被称为按秩转移。
注意这样会把每一个环算重两遍,而且还会出写走 i→j,j→i 的错误情况,需要去掉。
查看代码
#include<iostream>#include<cstdio>usingnamespace std;typedeflonglong i64;int n, m;
i64 f[600000][20];bool a[20][20];intmain(void){scanf("%d%d",&n,&m);for(int i =1; i <= m;++i){int u, v;scanf("%d%d",&u,&v);--u,--v;
a[u][v]= a[v][u]=true;}
i64 ans =0;for(int i =0; i < n;++i)
f[1<< i][i]=1;for(int i =1; i <(1<< n);++i)for(int j =0; j < n;++j){if(!f[i][j])continue;for(int k =0;k< n;++k){if(!a[j][k])continue;if((i &(-i))>(1<< k))continue;if((1<< k)& i){if((1<< k)==(i &(-i)))
ans += f[i][j];}else f[i |(1<< k)][k]+= f[i][j];}}printf("%lld\n",(ans - m)/2);return0;}
#include<iostream>#include<cstdio>#include<cstring>usingnamespace std;int n, l, r;int a[200005], f[200005];int L =1, R =0, q[200005];intmain(void){scanf("%d%d%d",&n,&l,&r);for(int i =0; i <= n;++i)scanf("%d", a + i);memset(f,0xbf,sizeof(f));
f[0]= a[0];int ans =-1e9;for(int i = l; i <= n;++i){while(L <= R && f[q[R]]<= f[i - l])--R;while(L <= R && q[L]+ r < i)++L;
q[++R]= i - l;
f[i]= f[q[L]]+ a[i];if(i + r > n) ans =max(ans, f[i]);}printf("%d\n", ans);return0;}
#include<iostream>#include<cstdio>#include<cstring>usingnamespace std;typedeflonglong i64;const i64 INF =1e18;int n, d, k;int x[500005], s[500005];
i64 f[500005];// 考虑到第 i 个位置的最大得分int Q[500005];boolP(int g){int l =(d > g ? d - g :1), r = d + g;int L =1, R =0, j =0;
i64 ans =-INF;memset(f,0xbf,sizeof(f));memset(Q,0,sizeof(Q));
f[0]=0;for(int i =1; i <= n;++i){while(j < i && x[i]- x[j]>= l){if(f[j]>-INF){while(L <= R && f[Q[R]]<= f[j])--R;
Q[++R]= j;}++j;}while(L <= R && x[i]- x[Q[L]]> r)++L;if(L <= R) f[i]= f[Q[L]]+ s[i];
ans =max(ans, f[i]);}return ans >= k;}intmain(void){scanf("%d%d%d",&n,&d,&k);
i64 sum =0;for(int i =1; i <= n;++i){scanf("%d%d", x + i, s + i);if(s[i]>0) sum += s[i];}if(sum < k)returnputs("-1"),0;int L =0, R =1000000001;while(L +1!= R){int mid = L + R >>1;if(P(mid)) R = mid;else L = mid;}printf("%d\n", R);return0;}
设 f(k,i,j) 代表考虑到第 k 个时间段,钢琴位置在 (i,j) 的最大滑动时间。每一个时间段我们都要枚举起点进行 DP,转移的过程中可以使用滑动窗口寻找最大值。
查看代码
#include<iostream>#include<cstdio>#include<cstring>#definerep(t)for(int i =1; i <= t;++i)usingnamespace std;constint dx[]={0,-1,1,0,0}, dy[]={0,0,0,-1,1};int n, m, sx, sy, k;int f[205][205];char s[205][205];
pair<int,int> Q[205];int L, R;voidwork(int x,int y,int len,int d){
L =1, R =0;for(int i =1; x >=1&& x <= n && y >=1&& y <= m;++i, x += dx[d], y += dy[d]){if(s[x][y]=='x'){ L =1, R =0;continue;}while(L <= R && Q[R].first + i - Q[R].second < f[x][y])--R;while(L <= R && i - Q[L].second > len)++L;
Q[++R]=make_pair(f[x][y], i);
f[x][y]= Q[L].first + i - Q[L].second;}}intmain(void){scanf("%d%d%d%d%d",&n,&m,&sx,&sy,&k);for(int i =1; i <= n;++i)scanf("%s", s[i]+1);memset(f,0xbf,sizeof(f));
f[sx][sy]=0;while(k--){int s, t, d;scanf("%d%d%d",&s,&t,&d);int len = t - s +1;if(d ==1)rep(m)work(n, i, len, d);elseif(d ==2)rep(m)work(1, i, len, d);elseif(d ==3)rep(n)work(i, m, len, d);elseif(d ==4)rep(n)work(i,1, len, d);}int ans =0;for(int i =1; i <= n;++i)for(int j =1; j <= m;++j)
ans =max(ans, f[i][j]);printf("%d\n", ans);return0;}