1. 程式人生 > >題解 P4277 【河城荷取的煙花】

題解 P4277 【河城荷取的煙花】

學校的一場考試:

點進去你也無法登陸的傳送門

技能比拼,分組方案,勇士的篝火

1.國王要嫁女兒啦!OI村莊裡的勇士們都想去試試,但是(不好的事都叫但是)......

2.經過OI村子裡的內部選拔,很多勇士都獲得了村長的青睞,村長為了......

3.OI村莊的勇士如願娶了國王女兒,村長打算......

講了由村長暗箱操作讓一群QIER勇士娶一位公主的故事

由情節就能看出資料水的一批

【河城荷取的煙花】的情節就美了很多

T3中出現了一個美觀扭曲的圖片:

題意:

現需燃盡一攤奇怪的繩子(學校裡是木棍,差不多),要找一個整點點火,使得燃盡時間最短

要明白火的(此題中)特性

  • 點燃後,火會沿著木棍向前方燃燒,可以點燃與它相接的木棍
  • 只能在木棍的兩端點燃

下面是一些沒多大用處的話:


知識點:構圖+最短路應用

在此題中是一個連通圖,如果我們直接構圖處理比較複雜~~根本不會~~。

我們對原問題進行轉換:

由於繩&棍與繩&棍之間只能在繩&棍的兩端或中間相交。我們把每根繩&棍拆分成兩根相等的小繩&棍,這樣,繩&棍的數量增加了一倍。

原問題就轉化為,繩&棍與繩&棍之間只能在繩&棍的兩端相交,這樣處理起來就比較方便。

我們以繩&棍為邊,繩&棍與繩&棍之間的交點為頂點,構建一個連通圖,問題變為尋找一個合適的頂點,使得點燃以後完全燃燒的時間最短。


有用的:

  • 一個繩&棍可拆成兩截小的繩&棍
  • (“顯然”是沒有原因的),燃燒時間等於點燃的頂點到圖中最遠點的時間,如下圖:


 由上述,需求最短路:

於是我們~~怎麼會有我~~可以利用Floyd's演算法求出任意兩點間的最短距離

餘下還需檢查每一條&根 繩&棍是否燃盡

當然,如果沒有完全燃燒,應求出剩餘邊燃燒所需最長時間

一些有(有?)用的話:


 對於燃燒時間為L的木棍,它的兩端被點燃的時刻為T1和T2

如果T1 = T2+L 或者是 T2 = T1+L,那麼燃燒到T1 和 T2 的最大時刻,這根木棍己經完全燃燒

如果T1與T2之間的時間差不等於L,那麼就說明火是從不同的路徑燃燒到這根木棍的兩端。火將從兩端向中間燃燒,並在木棍內的某個點燃完

在簡單情況中,如果是從兩端同時點燃,燃燒時間為L/2。

更一般地,如果T1與T2不等,我們設一端是從0時刻點燃,另一端是從T時刻點燃,那麼這根木棍的燃燒時間為

T + (L-(T-0))/2



即,一端先燃燒T時間後,另一端才開始燃燒,完全燃燒後的時間為

(L-(T-0))/2


 Floyd's :


 

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 double max(double a,double b)
 6 {
 7     if(a>b)return a;
 8     else return b;
 9 }
10 double min(double a,double b)
11 {
12     if(a<b)return a;
13     else return b;
14 }
15 int n;
16 int a1,a2,b1,b2,a1_5,b1_5,a[10001][10001];
17 double t;
18 double e[3001][3001],dis[3001][3001];
19 int tot=0;
20 bool mid[3001];
21 double minx=0x7fffffff,maxx=-0x7fffffff;
22 int main()
23 {
24     memset(dis,0x7fffffff,sizeof(dis));
25     memset(e,0x7fffffff,sizeof(e));
26     memset(mid,false,sizeof(mid));
27     scanf("%d",&n);
28     for(register int i=1;i<=n;i++)
29     {
30         scanf("%d%d%d%d%lf",&a1,&b1,&a2,&b2,&t);
31         a1=a1*2+400,b1=b1*2+400,a2=a2*2+400,b2=b2*2+400;
32         a1_5=(a1+a2)/2;
33         b1_5=(b1+b2)/2;
34         if(!a[a1][b1])a[a1][b1]=++tot;
35         if(!a[a2][b2])a[a2][b2]=++tot;
36         if(!a[a1_5][b1_5])
37         {
38             a[a1_5][b1_5]=++tot;
39             mid[tot]=true;
40         }
41         e[a[a1][b1]][a[a1_5][b1_5]]=t*1.00000/2;
42         e[a[a1_5][b1_5]][a[a1][b1]]=t*1.00000/2;
43         e[a[a2][b2]][a[a1_5][b1_5]]=t*1.00000/2;
44         e[a[a1_5][b1_5]][a[a2][b2]]=t*1.00000/2;
45         dis[a[a1][b1]][a[a1_5][b1_5]]=t*1.00000/2;
46         dis[a[a1_5][b1_5]][a[a1][b1]]=t*1.00000/2;
47         dis[a[a2][b2]][a[a1_5][b1_5]]=t*1.00000/2;
48         dis[a[a1_5][b1_5]][a[a2][b2]]=t*1.00000/2;
49     }
50     for(register int k=1;k<=tot;k++)
51     {
52         for(register int i=1;i<=tot;i++)
53         {
54             for(register int j=1;j<=tot;j++)
55             {
56                 if(dis[i][k]<0x7fffffff&&dis[k][j]<0x7fffffff)
57                 {
58                     dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
59                 }
60             }
61         }
62     }
63     for(register int k=1;k<=tot;k++)
64     {
65         if(mid[k])continue;
66         maxx=-0x7ffffff;
67         for(register int i=1;i<=tot;i++)maxx=max(maxx,dis[k][i]);
68         for(register int i=1;i<=tot;i++)
69         {
70             for(register int j=1;j<=tot;j++)
71             {
72                 if(dis[k][i]<e[i][j]+dis[k][j]&&dis[k][j]<dis[k][i]+dis[i][j])
73                 {
74                     maxx=max(maxx,max(dis[k][i],dis[k][j])+(e[i][j]-max(dis[k][i],dis[k][j])+min(dis[k][i],dis[k][j]))/2.0);
75                 }
76                 
77             }
78         }
79         minx=min(minx,maxx);
80     }
81     printf("%.4lf",minx);
82     return 0;
83 }

其實,只要不直接抄題解,Ctrl+c & Ctrl+v 挺好的

但是(前面說過“但是”不是一個很好的詞)上面這段程式碼交上洛谷不可能對

上程式碼是針對我校OJ的題,資料水的一批,擷取:


 【資料範圍】

100%的資料:

1<=n<=40;

|a|,|b|,|c|,|d|≤200, 0≤t≤1e7;


 運用鄰接矩陣等知識點,10分不錯了

做最短路方法太多了,例如SPFA,中國演算法當然要用(模板好套)

有了前面程式碼的基礎當然好寫

奉上:


 

  1 #include<bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 double max(double a,double b)
  6 {
  7     if(a>b)return a;
  8     else return b;
  9 }
 10 double min(double a,double b)
 11 {
 12     if(a<b)return a;
 13     else return b;
 14 }
 15 struct node
 16 {
 17     int from,to;
 18     double val;
 19     int nxt;
 20     node(int from=0,int to=0,double val=0,int nxt=0):from(from),to(to),val(val),nxt(nxt){};
 21 };
 22 int n,tot=0,len=0;
 23 double val;
 24 int a1,a2,b1,b2,am,bm;
 25 int f[10000];
 26 node edge[20000];
 27 int head[10000],a[4000][4000];
 28 double dis[10000];
 29 bool vis[10000];
 30 double ans=0x7fffffff;
 31 void add(int from,int to,double val)
 32 {
 33     edge[++len]=node(from,to,val,head[from]);
 34     head[from]=len;
 35 }
 36 int Num(int x,int y)
 37 { 
 38     if(!a[x][y])a[x][y]=++tot; 
 39     return a[x][y]; 
 40 }
 41 void SPFA(int s)
 42 {
 43     deque<int> q;
 44     memset(vis,0,sizeof(vis));
 45     memset(dis,127,sizeof(dis));
 46     dis[s]=0;
 47     q.push_front(s);
 48     vis[s]=1;
 49     while(!q.empty())
 50     {
 51         int cur=q.front();
 52         q.pop_front();
 53         vis[cur]=0;
 54         for(register int i=head[cur];i;i=edge[i].nxt)
 55         {
 56             int id=edge[i].to;
 57             if(dis[id]>dis[cur]+edge[i].val)
 58             {
 59                 dis[id]=dis[cur]+edge[i].val;
 60                 if(!vis[id])
 61                 {
 62                     vis[id]=true;
 63                     if(q.empty())q.push_front(id);
 64                     else
 65                     {
 66                         if(dis[id]<dis[q.front()])q.push_front(id);
 67                         else q.push_back(id);
 68                     }
 69                 }
 70             }
 71         }
 72     }
 73 }
 74 double calculate(int x)
 75 {
 76     double s=max(dis[edge[x].from],dis[edge[x].to]);
 77     s+=(edge[x].val-abs(dis[edge[x].from]-dis[edge[x].to]))/2;
 78     return s;
 79 }
 80 double check(int x)
 81 {
 82     SPFA(x);
 83     double ans=0;
 84     for(register int i=1;i<=len;i+=2)ans=max(ans,calculate(i));
 85     return ans;
 86 }
 87 int main()
 88 {
 89     scanf("%d",&n);
 90     for(register int i=1;i<=n;i++)
 91     {
 92         scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
 93         a1=a1*2+2000,a2=a2*2+2000,b1=b1*2+2000,b2=b2*2+2000;
 94         am=(a1+a2)/2,bm=(b1+b2)/2;
 95         scanf("%lf",&val);
 96         val/=2;
 97         add(Num(a1,b1),Num(am,bm),val);
 98         add(Num(am,bm),Num(a1,b1),val);
 99         add(Num(a2,b2),Num(am,bm),val);
100         add(Num(am,bm),Num(a2,b2),val);
101         f[Num(a1,b1)]=1;
102         f[Num(a2,b2)]=1;
103     }
104     for(register int i=1;i<=tot;i++)if(f[i])ans=min(check(i),ans);
105     printf("%.4lf",ans);
106     return 0;
107 }

這個SPFA自然是可過的

Floyd's演算法已經有大佬寫的很好,就不需要我的了~~

好不容易寫出來了,當然要發題解~~ ~~只有一篇,過的概率大一些~~

我要是過不去,豈不尷尬,求過o(╥﹏╥)o

謝謝Thanks♪(・ω・)ノ

再見ヾ( ̄▽ ̄)Bye~Bye~