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多萬不多的
把n個小弟是否接了任務的狀態壓縮,枚舉狀態
狀態中有多少個1就代表接到第幾個任務
每個狀態由狀態中消掉任意一個1的狀態轉移,即
其中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錯了”
每天都在