1. 程式人生 > >P1220 關路燈 動態規劃/深搜剪枝

P1220 關路燈 動態規劃/深搜剪枝

 

 

 

 

題目描述

某一村莊在一條路線上安裝了n盞路燈,每盞燈的功率有大有小(即同一段時間內消耗的電量有多有少)。老張就住在這條路中間某一路燈旁,他有一項工作就是每天早上天亮時一盞一盞地關掉這些路燈。

為了給村裡節省電費,老張記錄下了每盞路燈的位置和功率,他每次關燈時也都是儘快地去關,但是老張不知道怎樣去關燈才能夠最節省電。他每天都是在天亮時首先關掉自己所處位置的路燈,然後可以向左也可以向右去關燈。開始他以為先算一下左邊路燈的總功率再算一下右邊路燈的總功率,然後選擇先關掉功率大的一邊,再回過頭來關掉另一邊的路燈,而事實並非如此,因為在關的過程中適當地調頭有可能會更省一些。

現在已知老張走的速度為1m/s,每個路燈的位置(是一個整數,即距路線起點的距離,單位:m)、功率(W),老張關燈所用的時間很短而可以忽略不計。

請你為老張編一程式來安排關燈的順序,使從老張開始關燈時刻算起所有燈消耗電最少(燈關掉後便不再消耗電了)。

輸入輸出格式

輸入格式:

檔案第一行是兩個數字n(0<n<50,表示路燈的總數)和c(1<=c<=n老張所處位置的路燈號);

接下來n行,每行兩個資料,表示第1盞到第n盞路燈的位置和功率。

輸出格式:

一個數據,即最少的功耗(單位:J,1J=1W·s)。

 

動態規劃:

 

思路:

   f[i][j][0]表示關了[i,j]這個區間的燈,老張在第i盞燈的位置;

   f[i][j][1]表示關了[i,j]這個區間的燈,老張在第j盞燈的位置。

   sw[i][l]表示第i盞燈到第l盞燈的功率和。

於是:

dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][0]+(p[i+1]-p[i])*sw[i+1][l]);

dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][1]+(p[j]-p[i])*sw[i+1][l]);

dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+(p[j]-p[i])*sw[i][l-1]);

dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+(p[j]-p[j-1])*sw[i][l1]);

程式碼解釋:

#include<iostream>
#include<cstring>
using namespace std;
int w[55],z[55],n,c,sw[55][55],f[55][55][2],sum,ans;
bool v[55];
int main()
{
	cin>>n>>c;
	for(int i=1;i<=n;i++)
	{
	cin>>z[i]>>w[i];
	sum+=w[i];	
	}
    for(int i=1;i<=n;i++)
    {
    	sw[i][i]=w[i];
    	for(int l=i+1;l<=n;l++)
    	sw[i][l]=sw[i][l-1]+w[l];
	}
    memset(f,1,sizeof(f));//這裡初始化只需要比0大就ok
	f[c][c][1]=f[c][c][0]=0;
	for(int i=c;i>0;i--)
	for(int l=i;l<=n;l++)
	{
		f[i][l][0]=min(f[i][l][0],min(f[i+1][l][0]+(z[i+1]-z[i])*(sum-sw[i+1][l]),
		                                     f[i+1][l][1]+(z[l]-z[i])*(sum-sw[i+1][l])));
	    f[i][l][1]=min(f[i][l][1],min(f[i][l-1][0]+(z[l]-z[i])*(sum-sw[i][l-1]),
		                                     f[i][l-1][1]+(z[l]-z[l-1])*(sum-sw[i][l-1])));
	}
	ans=min(f[1][n][1],f[1][n][0]);
    cout<<ans<<endl;
    return 0;
}

 

搜尋:

 

思路:

對於每個路燈,去尋找左邊沒關掉的燈或者右邊沒有關掉的燈。

之後繼續深搜尋找

在搜尋中我們發現,如果此時的耗電量大於或等於記錄中的耗電量的話,

那麼這個搜尋是沒有必要繼續走下去的

程式碼解釋:

​
​
#include<iostream>
#include<cmath>
using namespace std;
int v[55],z[55],w[55],sum,ans=999999,n,c;
void dfs(int x,int tot,int sum,int m)
//x代表現在位於第幾個路燈,tot代表現在的耗電量,sum代表現在的總功率,m代表已經關了幾個燈
{
	if(m==n)
	{
		if(tot<ans)ans=tot;
		return ;
	}
	if(tot>=ans)return ;

    int p=x;
    while(p<=n&&v[p])p++;//尋找右邊第一個開著的燈
    if(p!=n+1)
    {
    v[p]=1;//關閉這個燈
    dfs(p,tot+sum*abs(z[p]-z[x]),sum-w[p],m+1);
    v[p]=0;    	
    }//向右走,別忘了回溯
    p=x;
    while(p>0&&v[p])p--;//尋找左邊第一個開著的燈
    if(p!=0)
    {
    v[p]=1;
    dfs(p,tot+sum*abs(z[p]-z[x]),sum-w[p],m+1);
    v[p]=0;	
    }//同理


}
int main()
{
	cin>>n>>c;
	for(int i=1;i<=n;i++)
	{
	    cin>>z[i]>>w[i];
		sum+=w[i];
	}
    sum-=w[c];
    v[c]=1;
    dfs(c,0,sum,1);
    cout<<ans<<endl;
    return 0;
}

​

​