T1:洪水
Description
一天,一个画家在森林里写生,突然爆发了山洪,他需要尽快返回住所中,那里是安全的。
森林的地图由R行C列组成,空白区域用点“.”表示,洪水的区域用“*”表示,而岩石用“X”表示,另画家的住所用“D”表示,画家用“S”表示。
有以下几点需要说明:
1、 每一分钟画家能向四个方向移动一格(上、下、左、右);
2、 每一分钟洪水能蔓延到四个方向的相邻格子(空白区域);
3、 洪水和画家都不能通过岩石区域;
4、 画家不能通过洪水区域(同时也不行,即画家不能移到某个格子,该格子在画家达到的同时被洪水蔓延到了,这也是不允许的);
5、 洪水蔓不到画家的住所。
给你森林的地图,编写程序输出最少需要花费多长时间才能从开始的位置赶回家中。
Input
输入第一行包含两个整数R和C(R,C<=50)。
接下来R行每行包含C个字符(“.”、“*”、“X”、“D”或“S”)。地图保证只有一个“D”和一个“S”。
Output
输出画家最快安全到达住所所需的时间,如果画家不可能安全回家则输出“KAKTUS”。
Sample Input
输入1:
3 3
D.*
…
.S.
输入2:
3 3
D.*
…
…S
输入3:
3 6
D…*.
.X.X…
…S.
Sample Output
输出1:
3
输出2:
KAKTUS
输出3:
6
简要思路:本题其实是一道简单的宽度优先搜索题(跑路题 ),只是每一层不能到达的格子有变化。在这里我们用深度标记法,用depth[x][y][0]记录洪水到达点[x][y]时所处的搜索深度,将0变成1记录画家到达点[x][y]时所处的搜索深度。先预处理depth[x][y][0],再宽搜即可。
本人曾考虑用IDA*(迭代加深搜)求解,然而时间复杂度过高,已被我本题的爆零证实是不可取的 。
//错误示范(IDA*)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 55;
int water[N * N][N][N] , room[N][N] , qx[N * N][2] , qy[N * N][2] , vis[N][N];
int xx[5] = { 0 , 1 , -1 , 0 , 0 };
int yy[5] = { 0 , 0 , 0 , 1 , -1 };
bool flag = false;
char ss[N];
int n , m , sx , sy , ex , ey , tot , pos;
inline bool judge( int k ) {
for ( int i = 1 ; i <= 4 ; ++i ) {
int xxx = ex + xx[i];
int yyy = ey + yy[i];
if ( xxx >= 1 && xxx <= n && yyy >= 1 && yyy <= m && ( !water[k][xxx][yyy] ) && ( !room[xxx][yyy] ) ) {
return true;
}
}
return false;
}
inline void doit( int k , int x , int y ) {
for ( int i = 1 ; i <= 4 ; ++i ) {
int xxx = x + xx[i];
int yyy = y + yy[i];
if ( xxx == ex && yyy == ey ) {
continue;
}
if ( xxx >= 1 && xxx <= n && yyy >= 1 && yyy <= m && ( !room[xxx][yyy] ) && ( !water[k][xxx][yyy] ) ) {
qx[++tot][pos ^ 1] = xxx;
qy[tot][pos ^ 1] = yyy;
water[k + 1][xxx][yyy] = 1;
}
}
return;
}
inline void dfs( int maxndepth , int x , int y , int dep ) {
if ( flag ) {
return;
}
if ( maxndepth == dep ) {
if ( ex == x && ey == y ) {
flag = true;
}
return;
}
for ( int i = 1 ; i <= 4 ; ++i ) {
int xxx = x + xx[i];
int yyy = y + yy[i];
if ( xxx >= 1 && xxx <= n && yyy >= 1 && yyy <= m && ( !room[xxx][yyy] ) && ( !water[dep + 1][xxx][yyy] ) && ( !vis[xxx][yyy] ) ) {
vis[xxx][yyy] = 1;
dfs( maxndepth , xxx , yyy , dep + 1 );
vis[xxx][yyy] = 0;
if ( flag ) {
return;
}
}
}
}
int main () {
scanf("%d",&n);
scanf("%d",&m);
pos = 1;
tot = 0;
for ( int i = 1 ; i <= n ; ++i ) {
scanf("%s",ss);
for ( int j = 1 ; j <= m ; ++j ) {
switch( ss[j - 1] ) {
case '*' : {
water[0][i][j] = 1;
qx[++tot][pos] = i;
qy[tot][pos] = j;
break;
}
case 'X' : {
room[i][j] = 1;
break;
}
case 'S' : {
sx = i;
sy = j;
break;
}
case 'D' : {
ex = i;
ey = j;
break;
}
}
}
}
int maxn , to , ans;
ans = -1;
for ( int i = 1 ; i <= n * n ; ++i ) {
to = tot;
tot = 0;
for ( int j = 1 ; j <= n ; ++j ) {
for ( int k = 1 ; k <= m ; ++k ) {
if ( water[i - 1][j][k] == 1 ) {
water[i][j][k] = 1;
}
}
}
while ( to ) {
doit( i - 1 , qx[to][pos] , qy[to][pos] );
to--;
}
if ( !judge(i) ) {
maxn = i;
break;
}
pos ^= 1;
}
for ( int i = 1 ; i <= maxn ; ++i ) {
dfs( i , sx , sy , 0 );
if ( flag ) {
ans = i;
break;
}
}
if ( ans != -1 ) {
printf("%d",ans);
} else {
printf("KAKTUS");
}
return 0;
}
//正解
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 55;
int depth[N][N][2]/*0记录洪水到达时的深度,1记录人到达时的深度*/ , room[N][N] , qx[N * N] , qy[N * N] , vis[N][N];
int xx[5] = { 0 , 1 , -1 , 0 , 0 };
int yy[5] = { 0 , 0 , 0 , 1 , -1 };
char ss[N];
int n , m , sx , sy , ex , ey , tai , hea;//tai , hea用于模拟队列
inline bool judge2( int x , int y , int dep ) {
return ( x >= 1 ) && ( x <= n ) && ( y >= 1 ) && ( y <= m ) && (!room[x][y]) && (!vis[x][y]) && dep < depth[x][y][0];
}//判断人
inline bool judge1( int x , int y ) {
return ( x >= 1 ) && ( x <= n ) && ( y >= 1 ) && ( y <= m ) && (!room[x][y]) && (!vis[x][y]) && ( x != ex || y != ey );
}//判断洪水(洪水淹不到住处)
int main () {
scanf("%d",&n);
scanf("%d",&m);
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = 1 ; j <= m ; ++j ) {
depth[i][j][0] = 2147483647;
}
}
for ( int i = 1 ; i <= n ; ++i ) {
scanf("%s",ss);
for ( int j = 1 ; j <= m ; ++j ) {
switch( ss[j - 1] ) {
case '*' : {
depth[i][j][0] = 0;
vis[i][j] = 1;
room[i][j] = 1;
qx[++tai] = i;
qy[tai] = j;
break;
}
case 'X' : {
room[i][j] = 1;
break;
}
case 'S' : {
depth[i][j][1] = 0;
sx = i;
sy = j;
break;
}
case 'D' : {
ex = i;
ey = j;
break;
}
default:{
break;
}
}
}
}
hea = 1;
if ( tai ) {
while ( hea <= tai ) {
int x = qx[hea];
int y = qy[hea];
for ( int i = 1 ; i <= 4 ; ++i ) {
int xxx = x + xx[i];
int yyy = y + yy[i];
if ( judge1( xxx , yyy ) ) {
qx[++tai] = xxx;
qy[tai] = yyy;
vis[xxx][yyy] = 1;
depth[xxx][yyy][0] = depth[x][y][0] + 1;
}
}
hea++;
}
}
memset( vis , 0 , sizeof(vis) );
hea = 1;
tai = 1;
qx[hea] = sx;
qy[hea] = sy;
vis[sx][sy] = 1;
while ( hea <= tai ) {
int x = qx[hea];
int y = qy[hea];
for ( int i = 1 ; i <= 4 ; ++i ) {
int xxx = x + xx[i];
int yyy = y + yy[i];
if ( judge2( xxx , yyy , depth[x][y][1] + 1 ) ) {
qx[++tai] = xxx;
qy[tai] = yyy;
vis[xxx][yyy] = 1;
depth[xxx][yyy][1] = depth[x][y][1] + 1;
}
}
hea++;
}
if ( depth[ex][ey][1] != 0 ) {
printf("%d",depth[ex][ey][1]);
} else {
printf("KAKTUS");
}
return 0;
}
T2:邦德I
Description
每个人都知道詹姆斯邦德,著名的007,但很少有人知道很多任务都不是他亲自完成的,而是由他的堂弟们吉米邦德完成(他有很多堂弟),詹姆斯已经厌倦了把一个个任务分配给一个个吉米,他向你求助。
每个月,詹姆斯都会收到一些任务,根据他以前执行任务的经验,他计算出了每个吉米完成每个任务的成功率,要求每个任务必须分配给不同的人去完成,每个人只能完成一个任务。
请你编写程序找到一个分配方案使得所有任务都成功完成的概率。
Input
输入第一行包含一个整数N,表示吉米邦德的数量以及任务的数量(正好相等,1<=N<=20)。
接下来N行,每行包含N个0到100之间整数,第i行的第j个数Aij表示吉米邦德i完成任务j成功的概率为Aij%
Output
输出所有任务成功完成最大的概率,结果保留6位小数。
Sample Input
输入1:
2
100 100
50 50
输入2:
2
0 50
50 0
输入3:
3
25 60 100
13 0 50
12 70 90
Sample Output
输出1:
50.000000
输出2:
25.000000
输出3:
9.100000
简要思路:本题是一道简单的状压题,不过本人不认为它和互不侵犯king类似(背地反对讲题老师不好吧 ),反而很像经典搜索题八皇后问题。如果用状压DP求解,则用dp[状态](1,0代表某人是否被派出去,任务按1到n的顺序统计)表示当前状态完成已选任务的最大可能性,用matrix[i][j]表示第i个人完成第j个任务的概率,具体实现看代码吧。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
double dp[1048580];//这里不要开大了,否则会MLE的(爆零感言)
int matrix[25][25] , er[22] , q[22];
int n;
inline void read( int & res ) {
res = 0;
int pd = 1;
char a = getchar();
while ( a < '0' || a > '9' ) {
if ( a == '-' ) {
pd = -pd;
}
a = getchar();
}
while ( a >= '0' && a <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
a = getchar();
}
res *= pd;
return;
}
int main () {
read(n);
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = 1 ; j <= n ; ++j ) {
read(matrix[i][j]);//为了快读也是拼了
}
}
er[0] = 1;
dp[0] = 100;
int top , tot;
for ( int i = 1 ; i <= n ; ++i ) {
er[i] = er[i - 1] * 2;
}
for ( int i = 1 ; i <= ( 1 << n ) - 1 ; ++i ) {
top = 0;
tot = 0;
for ( int k = 1 ; k <= n ; ++k ) {
if ( i & ( 1 << ( k - 1 ) ) ){
q[++top] = k;
tot++;//手动统计被派出的人数
}
}
while ( top ) {
int k = q[top];
top--;
dp[i] = max( dp[i] , dp[i - er[k - 1]] * (double)( matrix[k][tot] ) / 100.0 );
}
}
printf("%.6lf",dp[( 1 << n ) - 1]);
return 0;
}
T3:餐桌
Description
你家刚买了一套新房,想邀请朋友回来庆祝,所以需要一个很大的举行餐桌,餐桌能容纳的人数等于餐桌的周长,你想买一个能容纳最多人的餐桌,餐桌的边必须跟房间的边平行。
给你的房间的设计,计算最多能邀请的客人数。
Input
第一行包含两个整数R和C(1<=R,C<=2000),表示房子的长和宽。
接下来R行每行S个字符(中间没有空格),“.”表示空白区域,“X”表示有障碍物,餐桌所占区域必须是空白的。
Output
输出最多能要求的客人数量。
Sample Input
3 3
X.X
.X.
X.X
Sample Output
3
简要思路:首先,桌子是方形的,主人要有座位,答案要先减一。然后借着输入的机会用f[i][j]表示从格子[i][j]开始能向上拓展的最大值,很快就有人发现这是单调栈(我不知道这是什么 ),一旦发现一个可向上拓展的节点,如果可以,利用该节点向右拓展,吸纳新的可拓展节点,记录这些节点的最小可拓展值,不断更新答案 (其实是有计划地暴力枚举)。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 2005;
int n , m;
int f[N][N];//判断+计数
char ss[N];
int main () {
scanf("%d",&n);
scanf("%d",&m);
int ans = 0;
for ( int i = 1 ; i <= n ; ++i ) {
scanf("%s",ss);
for ( int j = 1 ; j <= m ; ++j ) {
if ( ss[j - 1] == '.' ) {
if ( f[i - 1][j] ) {
f[i][j] = f[i - 1][j] + 1;
} else {
f[i][j] = 1;
}
}
}
}
int len , minn;
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = 1 ; j <= m ; ++j ) {
if ( f[i][j] ) {
len = j;
minn = 3005;
while ( f[i][len] ) {
minn = min( minn , f[i][len] );
ans = max( ans , ( len - j + 1 + minn ) * 2 );
len++;
}
}
}
}
printf("%d",ans - 1);
return 0;
}
T4:自行车比赛
Description
自行车赛在一个很大的地方举行,有N个镇,用1到N编号,镇与镇之间有M条单行道相连,起点设在镇1,终点设在镇2。
问从起点到终点一共有多少种不同的路线。两条路线只要不使用完全相同的道路就被认为是不同的。
Input
第一行两个整数:N和M(1<=N<=10000,1<=M<=100000),表示镇的数量和道路的数量。
接下来M行,每行包含两个不同的整数A和B,表示有一条从镇A到镇B的单行道。
两个镇之间有可能不止一条路连接。
Output
输出不同路线的数量,如果答案超过9位,只需输出最后9位数字(不是取模,要考虑前导零)。如果有无穷多的路线,输出“inf”。
Sample Input
输入1:
6 7
1 3
1 4
3 2
4 2
5 6
6 5
3 4
输入2:
6 8
1 3
1 4
3 2
4 2
5 6
6 5
3 4
4 3
输入3:
31 60
1 3
1 3
3 4
3 4
4 5
4 5
5 6
5 6
6 7
6 7
…
…
…
28 29
28 29
29 30
29 30
30 31
30 31
31 2
31 2
Sample Output
输出1:
3
输出2:
inf
输出3:
073741824
简要思路:本题主要是tarjan缩点+拓扑排序,tarjan缩点后通过入度为零的点不断更新路径数,用d[cur]表示点cur的路径数(只有cur为1时要赋初值),则有:
d
[
c
u
r
]
=
∑
v
∈
f
a
t
h
e
r
[
c
u
r
]
d
[
v
]
d[cur] = \sum_{v\in father[cur]} d[v]
d[cur]=v∈father[cur]∑d[v]
要考虑重边,inf有三种情况,1所在的强连通分量不止一个点,2所在的强连通分量不止一个点,1到2经过的强连通分量不止一个点(就是路上有环)。不过本题数据水,没有inf的情况。
//能A但无inf
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define ll long long
#define mod 1000000000
using namespace std;
const int N = 10005 , M = 100005;
int n , m , bcnt , col , nu , top;
int low[N] , dfn[N] , st[N] , co[N] , siz[N] , x[M] , y[M] , head[N] , inde[N] , pd[N];
bool flag , fl;
ll d[N];
struct node{
int next;
int to;
}str[M];
queue<int> q;
inline void read( int & res ) {
res = 0;
int pd = 1;
char a = getchar();
while ( a < '0' || a > '9' ) {
if ( a == '-' ) {
pd = -pd;
}
a = getchar();
}
while ( a >= '0' && a <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
a = getchar();
}
res *= pd;
return;
}
inline void insert( int from , int to ) {
str[++bcnt].next = head[from];
str[bcnt].to = to;
head[from] = bcnt;
return;
}
inline void tarjan( int cur ) {
dfn[cur] = low[cur] = ++nu;
st[++top] = cur;
for ( int i = head[cur] ; i ; i = str[i].next ) {
int sn = str[i].to;
if ( !dfn[sn] ) {
tarjan(sn);
low[cur] = min( low[cur] , low[sn] );
} else if ( !co[sn] ) {
low[cur] = min( low[cur] , dfn[sn] );
}
}
if ( dfn[cur] == low[cur] ) {
co[cur] = ++col;
siz[col] = 1;
while( st[top] != cur ) {
co[st[top]] = col;
siz[col]++;
top--;
}
top--;
}
return;
}
inline bool cmp( int a , int b ) {
if ( x[a] != x[b] ) {
return x[a] < x[b];
} else {
return y[a] < y[b];
}
}
inline void dfs( int cur ) {
if ( cur == co[2] ) {
return;
}
for ( int i = head[cur] ; i ; i = str[i].next ) {
int sn = str[i].to;
dfs(sn);
if ( pd[sn] ) {
pd[cur] = 1;
inde[sn]++;
}
}
}
void bfs() {
while ( !q.empty() ) {
int cur = q.front();
q.pop();
for( int i = head[cur] ; i ; i = str[i].next ) {
int sn = str[i].to;
inde[sn]--;
d[sn] += d[cur];
if ( d[sn] >= mod ) {
fl = true;
d[sn] %= mod;
}
if ( !inde[sn] ) {
q.push(sn);
}
}
}
return;
}
void write( ll num ) {
int res[11];
int sum = 0;
if ( !fl ) {
printf("%lld",num);
return;
}
while (num) {
res[++sum] = num % 10;
num /= 10;
}
while ( sum < 9 ) {
res[++sum] = 0;
}
for ( int i = sum ; i >= 1 ; i-- ) {
printf("%d",res[i]);
}
}
int main () {
//freopen( "data9.in" , "r" , stdin );
read(n);
read(m);
for ( int i = 1 ; i <= m ; ++i ) {
read(x[i]);
read(y[i]);
insert( x[i] , y[i] );
}
for ( int i = 1 ; i <= n ; ++i ) {
if ( !dfn[i] ) {
tarjan(i);
}
}
if ( siz[co[1]] > 1 || siz[co[2]] > 1 ) {
printf("inf");
return 0;
}
memset( head , 0 , sizeof(head) );
bcnt = 0;
for ( int i = 1 ; i <= m ; ++i ) {
x[i] = co[x[i]];
y[i] = co[y[i]];
if ( x[i] != y[i] ) {
insert( x[i] , y[i] );
inde[y[i]]++;
}
}
for ( int i = 1 ; i <= col ; ++i ) {
if ( !inde[i] ) {
q.push(i);
}
}
flag = false;
fl = false;
d[co[1]] = 1;
bfs();
if ( flag ) {
printf("inf");
} else {
write(d[co[2]]);
}
return 0;
}
#pragma GCC optimize(2)//能考虑inf但在大数据面前会超时,吸氧优化都没用,鱼和熊掌不可得兼啊
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
#define mod 1000000000
using namespace std;
const int N = 10005 , M = 100005;
int n , m , bcnt , col , nu , top;
int low[N] , dfn[N] , st[N] , co[N] , siz[N] , x[M] , y[M] , head[N] , inde[N] , pd[N];
bool flag , fl;
ll d[N];
struct node{
int next;
int to;
}str[M];
queue<int> q;
inline void read( int & res ) {
res = 0;
int pd = 1;
char a = getchar();
while ( a < '0' || a > '9' ) {
if ( a == '-' ) {
pd = -pd;
}
a = getchar();
}
while ( a >= '0' && a <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
a = getchar();
}
res *= pd;
return;
}
inline void insert( int from , int to ) {
str[++bcnt].next = head[from];
str[bcnt].to = to;
head[from] = bcnt;
return;
}
inline void tarjan( int cur ) {
dfn[cur] = low[cur] = ++nu;
st[++top] = cur;
for ( register int i = head[cur] ; i ; i = str[i].next ) {
int sn = str[i].to;
if ( !dfn[sn] ) {
tarjan(sn);
low[cur] = min( low[cur] , low[sn] );
} else if ( !co[sn] ) {
low[cur] = min( low[cur] , dfn[sn] );
}
}
if ( dfn[cur] == low[cur] ) {
co[cur] = ++col;
siz[col] = 1;
while( st[top] != cur ) {
co[st[top]] = col;
siz[col]++;
top--;
}
top--;
}
return;
}
inline void dfs( int cur ) {
for ( register int i = head[cur] ; i ; i = str[i].next ) {
int sn = str[i].to;
if ( !pd[sn] ) {
dfs(sn);
}
if ( pd[sn] ) {
pd[cur] = 1;
inde[sn]++;
}
}
}
void bfs() {
while ( !q.empty() ) {
int cur = q.front();
q.pop();
for( register int i = head[cur] ; i ; i = str[i].next ) {
int sn = str[i].to;
if ( !pd[sn] ) {
continue;
}
inde[sn]--;
d[sn] += d[cur];
if ( d[sn] >= mod ) {
fl = true;
d[sn] %= mod;
}
if ( siz[sn] > 1 ) {
flag = true;
return;
}
if ( !inde[sn] ) {
q.push(sn);
}
}
}
return;
}
void write( ll num ) {
int res[11];
int sum = 0;
if ( !fl ) {
printf("%lld",num);
return;
}
while (num) {
res[++sum] = num % 10;
num /= 10;
}
while ( sum < 9 ) {
res[++sum] = 0;
}
for ( register int i = sum ; i >= 1 ; i-- ) {
printf("%d",res[i]);
}
}
int main () {
//freopen( "data9.in" , "r" , stdin );
read(n);
read(m);
for ( register int i = 1 ; i <= m ; ++i ) {
read(x[i]);
read(y[i]);
insert( x[i] , y[i] );
}
for ( int i = 1 ; i <= n ; ++i ) {
if ( !dfn[i] ) {
tarjan(i);
}
}
if ( siz[co[1]] > 1 || siz[co[2]] > 1 ) {
printf("inf");
return 0;
}
memset( head , 0 , sizeof(head) );
bcnt = 0;
for ( register int i = 1 ; i <= m ; ++i ) {
x[i] = co[x[i]];
y[i] = co[y[i]];
if ( x[i] != y[i] ) {
insert( x[i] , y[i] );
}
}
flag = false;
fl = false;
pd[co[2]] = 1;
dfs(co[1]);
if ( !pd[co[1]] ) {
printf("0");
return 0;
}
q.push(co[1]);
d[co[1]] = 1;
bfs();
if ( flag ) {
printf("inf");
} else {
write(d[co[2]]);
}
return 0;
}