0 洪水
一天, 一个画家在森林里写生,突然爆发了山洪,他需要尽快返回住所中,那里是安
全的。
森林的地图由R行C列组成,空白区域用点“.”表示,洪水的区域用“*”表示,而
岩石用“X”表示,另画家的住所用“D”表示,画家用“S”表示。
有以下几点需要说明:
1、 每一分钟画家能向四个方向移动一格(上、下、左、右)
2、 每一分钟洪水能蔓延到四个方向的相邻格子(空白区域)
3、 洪水和画家都不能通过岩石区域
4、 画家不能通过洪水区域(同时也不行,即画家不能移到某个格子,该格子在画家达到的同时被洪水蔓延到了,这也是不允许的)
5、 洪水蔓不到画家的住所。
给你森林的地图,编写程序输出最少需要花费多长时间才能从开始的位置赶回家中。
(R,C<=50)
直接搜就好
洪水蔓延,当画家在洪水肆虐前没能回家,画家就再也回不了家
画家无法追逐洪水的轨迹,洪水却能吞噬画家的足迹,命中注定有这一场不对等的博弈
#include <cstdio>
#include <cstring>
using namespace std;
const int dx[6]={1,-1,0,0};
const int dy[6]={0,0,-1,1};
int r,c,ans;
int a[55][55],w[55][55],d[55][55];
int v[4][3000],tv,th,h[4][3000];
void read(){
scanf("%d%d",&r,&c);
char ch[55];
memset(a,-1,sizeof a);
for (int i=1;i<=r;i++){
scanf("%s",ch+1);
for (int j=1;j<=c;j++)
if (ch[j]=='.') a[i][j]=0; else
if (ch[j]=='D') a[i][j]=1; else
if (ch[j]=='*') h[1][++th]=i,h[2][th]=j,h[3][th]=1; else
if (ch[j]=='S') v[1][++tv]=i,v[2][tv]=j,v[3][tv]=1;
}
}
void bfs(){
int hh=0,hv=0;
while (hh<th||hv<tv){
int x,y,c;
if (hh<th){
int c=h[3][++hh];
while (hh<=th&&c==h[3][hh]){
x=h[1][hh],y=h[2][hh];
w[x][y]=h[3][hh];
for (int i=0;i<4;i++)
if (a[x+dx[i]][y+dy[i]]==0&&w[x+dx[i]][y+dy[i]]==0){
w[x+dx[i]][y+dy[i]]=h[3][hh]+1;
h[1][++th]=x+dx[i],h[2][th]=y+dy[i],h[3][th]=h[3][hh]+1;
}
hh++;
}
hh--;
}
if (hv<tv){
int c=v[3][++hv];
while (hv<=tv&&c==v[3][hv]){
x=v[1][hv],y=v[2][hv];
d[x][y]=v[3][hv];
for (int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
if (a[xx][yy]>=0&&w[xx][yy]==0&&d[xx][yy]==0){
d[xx][yy]=v[3][hv]+1;
v[1][++tv]=xx,v[2][tv]=yy,v[3][tv]=v[3][hv]+1;
if (a[xx][yy]==1){
ans=d[xx][yy];
return;
}
}
}
hv++;
}
hv--;
}
}
}
int main(){
read();
bfs();
if (ans!=0) printf("%d",ans-1);
else printf("KAKTUS");
}
1 邦德I
每个人都知道詹姆斯邦德,著名的007,但很少有人知道很多任务都不是他亲自完成的,而是由他的堂弟们吉米邦德完成(他有很多堂弟),詹姆斯已经厌倦了把一个个任务分配给一个个吉米,他向你求助。
每个月,詹姆斯都会收到一些任务,根据他以前执行任务的经验,他计算出了每个吉米完成每个任务的成功率,要求每个任务必须分配给不同的人去完成,每个人只能完成一个任务。
请你编写程序找到一个分配方案使得所有任务都成功完成的概率。
输入第一行包含一个整数N,表示吉米邦德的数量以及任务的数量(正好相等,1<=N<=20)。
接下来N行,每行包含N个0到100之间整数,第i行的第j个数Aij表示吉米邦德i完成任务j成功的概率为Aij%
哎呀状压DP嘛,一眼看出来啦
但是考试时看错数据,以为2^20也就是状态数太大放不进数组,就打了暴搜50分做法
然而2^20也就100多万不多的
O ( n 2 n ) O(n2^n) O(n2n)
把n个小弟是否接了任务的状态压缩,枚举状态
状态中有多少个1就代表接到第几个任务
每个状态由状态中消掉任意一个1的状态转移,即
f
[
i
]
=
m
a
x
f
[
i
−
b
[
k
]
]
∗
a
[
s
]
[
k
]
/
100
f[i]=max{f[i-b[k]]*a[s][k]/100}
f[i]=maxf[i−b[k]]∗a[s][k]/100
其中s为当前状态任务总数,k为枚举的状态中任意一个1
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[22][22];
double ans;
int b[22],s[22];
double f[2097152];
void read(){
scanf("%d",&n);
b[0]=1;
for (int i=1;i<=n;i++){
b[i]=b[i-1]*2;
for (int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
}
}
void dp(){
f[0]=1;
for (int i=1;i<=b[n]-1;i++){
s[0]=0;
for (int j=1;j<=n;j++)
if ((i&b[j-1])>0)
s[++s[0]]=j;
for (int j=1;j<=s[0];j++)
f[i]=max(f[i],f[i-b[s[j]-1]]*a[s[0]][s[j]]/100);
}
}
int main(){
read();
dp();
printf("%.6f",f[b[n]-1]*100);
}
2 餐桌
你家刚买了一套新房,想邀请朋友回来庆祝,所以需要一个很大的举行餐桌,餐桌能容纳的人数等于餐桌的周长,你想买一个能容纳最多人的餐桌,餐桌的边必须跟房间的边平行。
给你的房间的设计,计算最多能邀请的客人数。
第一行包含两个整数R和C(1<=R,C<=2000),表示房子的长和宽。
接下来R行每行S个字符(中间没有空格),“.”表示空白区域,“X”表示有障碍物,餐桌所占区域必须是空白的。
矩形的高度用up[i][j]表示,即格子(i,j)向上多少个格子到障碍物格子
一行一行枚举
这时就把每一行的矩形从左向右加入队列,当后加的矩形比前面的矩形矮时,要弹出高的矩形高出的部分,并将原矩形更新答案,然后把消掉高出部分的矩形加入后加的矩形,也就是后加的矩形宽度增加。
加入答案时,用矩形的周长=(长+宽)/ 2
emm…
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int r,c,ans;
int a[2005][2005];
int up[2005][2005];
int f[2005],w[2005],cnt;
void read(){
scanf("%d%d",&r,&c);
for (int i=1;i<=r;i++){
char ch=getchar();
int j=0;
while (ch!='X'&&ch!='.') ch=getchar();
while (ch=='X'||ch=='.'){
j++;
if (ch=='.') a[i][j]=1;
ch=getchar();
}
}
for (int i=0;i<=r;i++){
for (int j=1;j<=c;j++)
if (a[i][j]==0){
for (int k=i+1;k<=r&&a[k][j]==1;k++)
up[k][j]=up[k-1][j]+1;
}
}
}
void work(){
for (int i=1;i<=r;i++){
for (int j=1;j<=c+1;j++){
if (a[i][j]==0){
while (cnt){
ans=max(ans,(f[cnt]+w[cnt])*2);
w[cnt-1]+=w[cnt];
cnt--;
}
}
else{
if (up[i][j]>f[cnt]) f[++cnt]=up[i][j],w[cnt]=1; else
if (up[i][j]==f[cnt]) w[cnt]++; else{
int cw=0;
while (up[i][j]<f[cnt]){
ans=max(ans,(f[cnt]+w[cnt])*2);
cw+=w[cnt];
cnt--;
}
f[++cnt]=up[i][j],w[cnt]=cw+1;
while (up[i][j]==f[cnt-1]) w[cnt-1]+=w[cnt],cnt--;
}
}
}
}
}
int main(){
read();
work();
printf("%d",ans-1);
}
3 自行车比赛
自行车赛在一个很大的地方举行,有N个镇,用1到N编号,镇与镇之间有M条单行道相连,起点设在镇1,终点设在镇2。
问从起点到终点一共有多少种不同的路线。两条路线只要不使用完全相同的道路就被认为是不同的。
第一行两个整数:N和M(1<=N<=10000,1<=M<=100000),表示镇的数量和道路的数量。
接下来M行,每行包含两个不同的整数A和B,表示有一条从镇A到镇B的单行道。
两个镇之间有可能不止一条路连接。
用拓补排序排出顺序,按照拓补序递推,f [ i ] = f [ i ] + f [ j ] ,其中 i 由 j 点走来
这里是拓补排序简单描述
————————————————————
不难看出该算法的实现十分直观,关键在于需要维护一个入度为0的顶点的集合:
每次从该集合中取出(没有特殊的取出规则,随机取出也行,使用队列/栈也行,下同)一个顶点,将该顶点放入保存结果的List中。
紧接着循环遍历由该顶点引出的所有边,从图中移除这条边,同时获取该边的另外一个顶点,如果该顶点的入度在减去本条边之后为0,那么也将这个顶点放到入度为0的集合中。然后继续从集合中取出一个顶点…………
当集合为空之后,检查图中是否还存在任何边,如果存在的话,说明图中至少存在一条环路。不存在的话则返回结果List,此List中的顺序就是对图进行拓扑排序的结果。
描述转自 https://blog.csdn.net/dm_vincent/article/details/7714519
————————————————————
#include <cstdio>
#include <cstring>
using namespace std;
const long long mod=1000000000;
const int N=10004,M=100005;
int n,m,bz;
long long d[N];
int ls[N],y[M],ne[M],b[N];
int a[N],cnt,r[N];
void read(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
ne[i]=ls[u];ls[u]=i;y[i]=v;
r[v]++;
}
}
int dfs(int x){
b[x]=1;
if (x==2) {b[x]=2;}
int bz=0;
for (int i=ls[x];i;i=ne[i]){
if (b[y[i]]==0)
bz|=dfs(y[i]);
else if (b[y[i]]==2) bz|=1;
}
if (bz) b[x]=2;
if (b[x]==2) return 1;
return bz;
}
void victor(){
for (int i=1;i<=n;i++)
if (r[i]==0&&b[i]==2) a[++cnt]=i;
for (int i=1;i<=n;i++)
if (b[i]!=2)
for (int j=ls[i];j;j=ne[j])
r[y[j]]--;
d[1]=1;
for (int i=1;i<=cnt;i++){
int x=a[i];
for (int j=ls[x];j;j=ne[j])
if (b[y[j]]==2){
if (d[y[j]]+d[x]>=mod)
bz=1;
if (y[j]==2){
y[j]=2;
}
d[y[j]]=(d[y[j]]+d[x])%mod;
r[y[j]]--;
if (r[y[j]]==0) a[++cnt]=y[j];
}
}
}
int main(){
read();
dfs(1);
victor();
if (bz){
int j=0;
for (int i=d[2];i;i/=10) j++;
if (j<9)
for (int i=1;i<=9-j;i++)
printf("0");
}
printf("%d",d[2]);
}
“为什么这个DFS死循环?”
“不知道,一定是你写错了”
“哦,可是我是co你的呀”
“……那一定是你co错了”
每天都在