11.2
T1 下棋
Sol
直接记搜就可以了,记\(f[i][j][k][l]\)表示当前放了\(i\)个白棋\(j\)个黑棋,直至目前白棋\(-\)黑棋最大值为\(k\),黑棋\(-\)白棋最大值为\(l\)。
能放黑棋白棋就枚举放置即可。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=160,maxk=23,p=1000000007;
namespace io
{
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
inline void print(int x)
{
static int s[20],len;
len=0;
if(x<0)putchar('-'),x=-x;
if(x==0)
{
putchar('0');return;
}
while(x)
{
s[++len]=x%10;
x/=10;
}
for(int i=len;i;i--)putchar(s[i]+'0');
return;
}
}
using namespace io;
int f[maxn][maxn][maxk][maxk];
int n,m,k;
inline int dfs(int x,int y,int mxx,int mxy)
{
if(~f[x][y][mxx][mxy])return f[x][y][mxx][mxy];
int &ans=f[x][y][mxx][mxy];
ans=0;
if(x==n&&y==m)return ans=1;
if(x<n&&mxx<k)ans+=dfs(x+1,y,mxx+1,max(0,mxy-1));
if(y<m&&mxy<k)ans+=dfs(x,y+1,max(0,mxx-1),mxy+1);
if(ans>=p)ans-=p;
return ans;
}
int main()
{
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
n=read();m=read();k=read();
memset(f,-1,sizeof(f));
dfs(0,0,0,0);
print(f[0][0][0][0]);
return 0;
}
T2 出租车
Sol
考场上多了一维还写的直接DP,还带数据分治。这导致我对于我那8kb代码无能为力……
考虑\(dp[i][now][gpa][gpb][gpc][gpd]\)表示当前第\(i\)个人要上车,现在车在\(now\),目前车上四个人的目的地。
发现车子满载的时候根本没法上人,所以可以省去一维。那么转移方程就是先看能不能把一个人送下车,然后判断是否全部人下车且无人要上车,然后如果车上已经三个人了就枚举载上第四个人以后谁先下车,最后是再拉一个人上车。
细节还好,不算很多,且代码复制粘贴地方多,看着挺整齐。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=2010,maxk=11;
namespace io
{
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
inline void print(int x)
{
static int s[20],len;
len=0;
if(x<0)putchar('-'),x=-x;
if(x==0)
{
putchar('0');return;
}
while(x)
{
s[++len]=x%10;
x/=10;
}
for(int i=len;i;i--)putchar(s[i]+'0');
return;
}
}
using namespace io;
int n,k;
int u[maxn],v[maxn];
int f[maxn][maxk][maxk][maxk][maxk];
inline int dfs(int i,int now,int gpa,int gpb,int gpc)
{
if(~f[i][now][gpa][gpb][gpc])return f[i][now][gpa][gpb][gpc];
int &rst=f[i][now][gpa][gpb][gpc];rst=1000000000;
if(gpa)rst=min(rst,dfs(i,gpa,0,gpb,gpc)+abs(now-gpa)+1);
if(gpb)rst=min(rst,dfs(i,gpb,gpa,0,gpc)+abs(now-gpb)+1);
if(gpc)rst=min(rst,dfs(i,gpc,gpa,gpb,0)+abs(now-gpc)+1);
if(i==n+1)
{
if(gpa==0&&gpb==0&&gpc==0)return rst=0;
return rst;
}
if(gpa&&gpb&&gpc)
{
rst=min(rst,dfs(i+1,gpa,v[i],gpb,gpc)+abs(now-u[i])+abs(u[i]-gpa)+2);
rst=min(rst,dfs(i+1,gpb,gpa,v[i],gpc)+abs(now-u[i])+abs(u[i]-gpb)+2);
rst=min(rst,dfs(i+1,gpc,gpa,gpb,v[i])+abs(now-u[i])+abs(u[i]-gpc)+2);
rst=min(rst,dfs(i+1,v[i],gpa,gpb,gpc)+abs(now-u[i])+abs(u[i]-v[i])+2);
}else
{
if(!gpa)rst=min(rst,dfs(i+1,u[i],v[i],gpb,gpc)+abs(now-u[i])+1);
if(!gpb)rst=min(rst,dfs(i+1,u[i],gpa,v[i],gpc)+abs(now-u[i])+1);
if(!gpc)rst=min(rst,dfs(i+1,u[i],gpa,gpb,v[i])+abs(now-u[i])+1);
}
return rst;
}
int main()
{
freopen("taxi.in","r",stdin);
freopen("taxi.out","w",stdout);
n=read();k=read();
for(int i=1;i<=n;i++)u[i]=read(),v[i]=read();
memset(f,-1,sizeof(f));
printf("%d\n",dfs(1,1,0,0,0));
return 0;
}
T3 堆石子
Sol
显然30分就是直接枚举每一堆选不选。
然后套个折半搜索就过了。。。我是傻逼。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=40;
namespace io
{
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
inline void print(int x)
{
static int s[20],len;
len=0;
if(x<0)putchar('-'),x=-x;
if(x==0)
{
putchar('0');return;
}
while(x)
{
s[++len]=x%10;
x/=10;
}
for(int i=len;i;i--)putchar(s[i]+'0');
return;
}
}
using namespace io;
int n,s[maxn],m,ans=0;
int d1[1<<20],d2[1<<20];
int main()
{
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
n=read();m=read();
int half=n/2;
for(int i=0;i<n;i++)s[i]=read()%m;
for(int i=0;i<(1<<half);i++)
{
for(int j=0;j<half;j++)
{
if(i&(1<<j))
{
d1[i]+=s[j];if(d1[i]>=m)d1[i]-=m;
}
}
}
for(int i=0;i<(1<<n-half);i++)
{
for(int j=0;j<n-half;j++)
{
if(i&(1<<j))
{
d2[i]+=s[j+half];if(d2[i]>=m)d2[i]-=m;
}
}
}
sort(d1,d1+(1<<half));
sort(d2,d2+(1<<n-half));
int now=(1<<n-half)-1;
for(int i=0;i<(1<<half);i++)
{
while(now>=0&&d1[i]+d2[now]>=m)now--;
if(now>=0)ans=max(ans,d1[i]+d2[now]);
}
print(ans);putchar('\n');
return 0;
}
T4 不稳定的导弹系统
Sol
考场降智以为是状压DP优化。。。
考虑先假设每个系统都能打到自己能打到的最大的点,然后在想如何调整使得保证合法且答案最大。假设每个格点之间连边变成机会成本,也就是替换成打这个目标会损失的答案。那么这样构造出一个图,要求出图的最小割就是最小调整代价。
但是有可能出现多行多列交叉,所以把每个点拆分成横点和竖点,横点向竖点连inf边,源点向南北向路径连inf边,东西向路径向汇点连inf边,点与点间连机会成本值边,剩下的网络流基操。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5010,maxm=100010,inf=1000000000;
namespace io
{
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
inline void print(int x)
{
static int s[20],len;
len=0;
if(x<0)putchar('-'),x=-x;
if(x==0)
{
putchar('0');return;
}
while(x)
{
s[++len]=x%10;
x/=10;
}
for(int i=len;i;i--)putchar(s[i]+'0');
return;
}
}
using namespace io;
struct edge
{
int to,next,v;
}e[maxm];
int h[maxn],ei=1;
inline void add(int x,int y,int v)
{
e[++ei]=(edge){y,h[x],v};
h[x]=ei;
e[++ei]=(edge){x,h[y],0};
h[y]=ei;return;
}
int n,m,S,T,ans;
int p(int x,int y){return ((x-1)*m+y)*2;}
int q(int x,int y){return ((x-1)*m+y)*2-1;}
int a[60][60];
int dep[maxn];
queue<int>qu;
inline bool bfs()
{
memset(dep,0,sizeof(dep));
dep[S]=1;qu.push(S);
while(!qu.empty())
{
int x=qu.front();qu.pop();
for(int i=h[x];i;i=e[i].next)
{
int to=e[i].to;
if(dep[to]||e[i].v==0)continue;
dep[to]=dep[x]+1;
qu.push(to);
}
}
return dep[T]>0;
}
inline int dfs(int x,int maxflow)
{
if(x==T)return maxflow;
int flow=0;
for(int i=h[x];i;i=e[i].next)
{
int to=e[i].to;
if(dep[to]!=dep[x]+1||e[i].v==0)continue;
int rst=dfs(to,min(maxflow-flow,e[i].v));
if(rst==0)dep[to]=0;
e[i].v-=rst;e[i^1].v+=rst;
flow+=rst;
if(flow==maxflow)break;
}
return flow;
}
inline void dinic()
{
while(bfs())ans-=dfs(S,inf);
return;
}
int main()
{
freopen("missile.in","r",stdin);
freopen("missile.out","w",stdout);
n=read();m=read();S=n*m*2+1;T=S+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=read();
add(p(i,j),q(i,j),inf);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int mx=0;
if(a[i][j]==-1)
{
add(S,p(i,j),inf);
for(int k=1;k<=i;k++)mx=max(mx,a[k][j]);
for(int k=2;k<=i;k++)
{
add(p(k,j),p(k-1,j),mx-max(a[k][j],0));
}
}
if(a[i][j]==-2)
{
add(S,p(i,j),inf);
for(int k=i;k<=n;k++)mx=max(mx,a[k][j]);
for(int k=i;k<n;k++)
{
add(p(k,j),p(k+1,j),mx-max(a[k][j],0));
}
}
if(a[i][j]==-3)
{
add(q(i,j),T,inf);
for(int k=1;k<=j;k++)mx=max(mx,a[i][k]);
for(int k=2;k<=j;k++)
{
add(q(i,k-1),q(i,k),mx-max(a[i][k],0));
}
}
if(a[i][j]==-4)
{
add(q(i,j),T,inf);
for(int k=j;k<=m;k++)mx=max(mx,a[i][k]);
for(int k=j;k<m;k++)
{
add(q(i,k+1),q(i,k),mx-max(a[i][k],0));
}
}
ans+=mx;
}
}
dinic();
print(ans);putchar('\n');
return 0;
}