hfut 1247 H 技術員BangFu

 這題的確是一道好題,很好的將狀態dp以及圖論的最短路徑,這裏上面的權值表示的花費的錢,另外還有很多約束問題,

 首先大體描述下這道題,就是一個技術員,遍歷N個節點,首先他一開始在0號節點,且是星期一,然後遍歷1..N-1 編號的節點,這裏要求每個節點只能走一次,而且必須每個節點都得走到,最後還要回到0號節點,而且從一個定點到另一個頂點是要花費p天的時間,還要要一定的錢,而且在每個節點也至少得待一天的時間,最後的最後,要我們解決的問題是,首先判定他是否能夠每個節點都能走到,另外,如果能又是否能在星期六或星期天到,若是,那麼我們至少得花多少錢呢!

  問題已經很清晰了,現在的問題就是如何求了,按照我開始的思路,首先判定這是否是一個遍歷所有節點的迴路,然後既然這是一個狀態dp的問題,那麼肯定是涉及到狀態的改變的,那麼狀態是什麼呢,這裏就是走過的節點,可以作爲狀態,如果已經走過,那麼爲1,否則爲0,那麼N個節點可以表示成一個二進制串,然後相對應的十進制值就是狀態數,如上,我們如何判斷狀態的合法性,這裏就是走過的節點就不要回去了,所以就很好判斷了,也就是說每一位只能置1一次,好,搞過之後,我們在找狀態方程,因爲這裏好像很隱藏,

因爲問題描述當中,有要求的天數,和花費的錢數,那麼這個方程到底怎麼表示呢!

 這裏設dp[state][des][d]  這個方程的意思就是狀態爲state時,且到達了des節點,且此時是星期,另外這個值就是達到這點所花費的錢   0<=state<2^N,1<=des<=N-1,0<=d<7

   然後就是初始化問題,dp[0][0][0]=inf,dp[1][0][0]=0,然後就是用bfs遍歷找出求最短路勁了,這裏最短路勁也好求,首先在輸入的時候,建立鄰接表,bfs的用隊列維護,這裏就如上所述,所涉及的要保存的信息很多,於是我們想到結構體,用結構體來表示節點以及邊,這樣,這個過程就很好辦了,當然我們知道bfs的過程中,還有一個visit的,這裏也需要建立類似的標記,這裏要和dp有同樣的大小,

 這樣搞過之後,最後就很好辦了,首先dp[2^N-1][1....N][D] ,循環遍歷在目標1....N個節點中看看有沒到0號節點的,兩個fou循環,找出最小值,同時判斷是否能夠在星期六和星期天道,這些都很好辦了,,接下來就可以去是實現了,,就是苦力活了,,有許多的細節,要考慮全面!

  

   下面的代碼就是寫給自己看的,成功AC,,要實現就自己寫吧!

 #include<iostream>

#include<vector>

#include<queue>

#include<algorithm>

#define inf 0x7fffffff

using namespace std; 

struct edge

{

  int y,p,t;  //含義如題所述 

};

struct node

{

  int s,n,d;    //s表示當前狀態,n表示節點標號,d表示經過的天數 

};

vector<edge> num[11];

int dp[1<<10][11][7];

int visit[1<<10][11][7];

queue<node> qu;

int N,M;

void sloveinput()

{

  //初始化

  for(int i=0;i<(1<<N);i++)

   for(int j=0;j<N;j++)

    for(int k=0;k<7;k++)

      {

        dp[i][j][k]=inf;

        visit[i][j][k]=0;

      }

  for(int i=0;i<N;i++)

   num[i].clear();

  edge tmp;

  for(int i=0;i<M;i++)

  {

    int a;

    cin>>a>>tmp.y>>tmp.p>>tmp.t;

    num[a].push_back(tmp);

  }

  //鄰接表以建立好 

}

void slovedp()

{

  //這個函數就是要求dp[][][]裏面能求的值,要用隊列 

  node start;

  edge tmp; 

  start.s=1;start.n=0;start.d=0;

  // 

  tmp.y=0;tmp.p=0;tmp.t=0;

  //這裏從0號節點開始,00...001 所以狀態s1,但是節點y0,當然p,和t

 // qu.clear();

  dp[1][0][0]=0; 

  visit[1][0][0]=1;

  qu.push(start);

  while(!qu.empty())  //開始bfs

  {

    node tp=qu.front();

    qu.pop();

    for(int i=0;i<num[tp.n].size();i++)  //在這些臨邊遍歷 

    {

      if(!(tp.s&(1<<num[tp.n][i].y))) 

       //這說明以前這個節點還是沒有走過的,那麼可擴展,

       //這裏就是判定合法狀態的條件,狀態dp一般到要這樣

       {

          //建立節點保存,併入隊列,

          node node1;

          node1.s=tp.s|(1<<num[tp.n][i].y);

          node1.n=num[tp.n][i].y;

          node1.d=(tp.d+num[tp.n][i].t+1)%7;

          //到這裏後,如何判定入棧呢

          if(dp[tp.s][tp.n][tp.d]+num[tp.n][i].p<dp[node1.s][node1.n][node1.d])

          //如果通過上一個狀態 花費加上到當前節點的花費小於 當前狀態的花費,則擴充,入棧,//這裏是重要的地方,狀態轉移方程 

          {

          dp[node1.s][node1.n][node1.d]=dp[tp.s][tp.n][tp.d]+num[tp.n][i].p;  //更新吧 

          if(!visit[node1.s][node1.n][node1.d])

           {

             visit[node1.s][node1.n][node1.d]=1;

             qu.push(node1);

           }

          }

       } 

    } 

  } 

void sloveans()

{

   //這個收尾就很簡單嘍

   int end=(1<<N)-1;

   int flag1=0,flag2=0;

   int ans=inf; 

   for(int i=1;i<=N-1;i++)  //遍歷

    for(int j=0;j<num[i].size();j++)

     {

       if(num[i][j].y==0)  //說明有回到0節點的

        {

           for(int dd=0;dd<7;dd++)  //判定是否能在週六或週日到 

            {

              if(dp[end][i][dd]<inf)  

              //說明有成功到達0節點的,且遍歷了所有節點,這裏一開始寫成裏 dp[end][num[i][j].y][dd]<inf,細節問題一定要注意啊 

              {

                flag1=1;    

                //再是第二個條件,看是否能在週六或週日到達 

                if(((dd+num[i][j].t)%7)>=5&&ans>dp[end][i][dd]+num[i][j].p)

                 //這裏把星期一去掉,默認從0,那麼經過t天后,如果是5,6,就說明了是能在星期六和星期日到達的 

                 //並且找到滿足的最小花費值 

                 {

                   flag2=1;

                   ans=dp[end][i][dd]+num[i][j].p; 

                 } 

              } 

            }

        } 

     }

    if(!flag1) cout<<"It's not my thing!"<<endl;  

    else if(!flag2) cout<<"Oh, My god!"<<endl;

    else cout<<ans<<endl;

}

int main()

{

    while(cin>>N>>M)

    {

      sloveinput();

      slovedp();

      sloveans(); 

    }

}

發佈了37 篇原創文章 · 獲贊 1 · 訪問量 205萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章