1. 程式人生 > >【動態規劃】ZZNU-OJ- 2054 : 油田

【動態規劃】ZZNU-OJ- 2054 : 油田

tex 搜索 n) 正整數 頁面 復制 最優解 使用 包含

2054 : 油田

(一個神奇的功能:點擊上方文字進入相應頁面)

時間限制:1 Sec 內存限制:32 MiB
提交:49 答案正確:6

提交 狀態 討論區

題目描述

在太平洋的一片海域,發現了大量的油田!

為了方便開采這些能源,人們將這些油田從1到n進行編號,

人們在開采這些油田時,有三種開采方式,分別為方式A,方式B,方式C。

用不同的方式去開采這些油田所消耗的資金不同,為了防止共振導致的油井坍塌,相鄰編號的油田不能使用同一種開采方式。

我們希望你求出開采這n個油田所需要消耗的最小資金,並輸出開采每個油田所采用的方式。

輸入

先輸入一個整數T(0 < T <= 100),代表有T組測試數據。對於每組數據,第一行輸入一個正整數n(n<1000)代表油田數目,

接下來n行,每一行包含三個整數。第i(2 <= i <= n+1)行的這三個數代表著開采編號為i-1的油田分別采用A,B,C三種方式開采所消耗的資金。

輸出

對於每一組測試樣本,先輸出樣本編號,接下來輸出一個整數,代表著開采這n個油田所需要消耗的最小資金,然後按編號從小到大的順序輸出開采每個油田所采用的方式。每一組測試樣本的輸出占一行。

樣例輸入

復制
1
2
4 8 3
2 1 4

樣例輸出

復制
Case 1: 4 CB

提示

看著像是搜索題目,但是時間復雜度太大了,最壞的情況會嚴重超時!如下:

技術分享圖片
#include <iostream>
#include
<stdio.h> #include<algorithm> #include<string.h> #include<stdlib.h> using namespace std; #define N 1009 const int inf=0x3f3f3f3f; int ans,n,tem[N],road[N],cost[N][4]; //tem中 1A ,2B ,3C void factroad() { int i,j,k; for(i=1;i<=n;i++) road[i]=tem[i]; return ; }
void dfs(int sum,int step) //step表示當前! { if(step==n+1) { if(ans>sum) { ans=sum;factroad(); } return ; } for(int i=1;i<=3;i++) //當前step的三種選擇 { if(i!=tem[step-1]) { tem[step]=i; dfs(sum+cost[step][i],step+1); } } } int main() { int i,j,k,m,T,cas=0; scanf("%d",&T); while(T--) { scanf("%d",&n); //memset(vis,0,sizeof(vis)); for(i=1;i<=n;i++) scanf("%d%d%d",&cost[i][1],&cost[i][2],&cost[i][3]); ans=inf; dfs(0,1); printf("Case %d: %d ",++cas,ans); for(i=1;i<=n;i++) printf("%c",A+road[i]-1); printf("\n"); } return 0; }
View Code

該題正解,如下:

技術分享圖片
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
using namespace std;
#define N 1009
const int inf=0x3f3f3f3f;
int sum;  //保存最終最小和
int ans[N];  //ans數組記錄最優答案路徑
int n,c[N][4]; //  c數組中c[i][j]表示第i個油田的第j種開采方式的花費
int dp[N][4];  //dp數組用於動態規劃查找最優解;
            //dp[i][j]表示第i個油田采用第j種開采方式時,整個1--i個油田需要的最小開銷總和
int pre[N][4];  //pre前綴數組用於保存dp過程中的路徑

void fact(int x,int y) //輸出路徑到ans數組裏
{
    sum=dp[x][y];
    while(x>=1)
    {
        ans[x-1]=pre[x][y];
        y=pre[x][y];
        x--;
    }
}
void test(int n){  //簡單的調試函數
   /* for(int i=1;i<=n;i++)
        printf("%3d",i);
    printf("\n");*/
    for(int i=1;i<=n;i++)
    {
        printf("i%d: ",i);
        for(int j=1;j<=n;j++)
            printf("%3d",dp[i][j]);
        printf("\n");
    }
}
int main()
{
    int i,j,k,m,T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        memset(c,0,sizeof(c));  //這裏很重要,C++裏數組不賦初值!在下面程序中會訪問到c[n+1][]數組裏的數
        scanf("%d",&n);
        //memset(vis,0,sizeof(vis));
        for(i=1;i<=n;i++)
            scanf("%d%d%d",&c[i][1],&c[i][2],&c[i][3]);
        memset(dp,0,sizeof(dp));  //dp[0][]數組裏的數會用到

        for(i=1;i<=n+1;i++)
        {
            for(j=1;j<=3;j++)  //
            {
                if(j==1)
                {
                    dp[i][j]=c[i][j]+min(dp[i-1][2],dp[i-1][3]);
                    if(dp[i-1][2]>dp[i-1][3])  //記錄路徑,這個沒法同上一步用min()
                        pre[i][j]=3;      //這裏歪打正著了,存在相等時要選擇編號小的記錄入路徑,不然會出錯
                    else
                        pre[i][j]=2;

                }
                else if(j==2)
                {
                    dp[i][j]=c[i][j]+min(dp[i-1][1],dp[i-1][3]);
                    if(dp[i-1][1]>dp[i-1][3])
                        pre[i][j]=3;
                    else
                        pre[i][j]=1;
                }
                else
                {
                    dp[i][j]=c[i][j]+min(dp[i-1][1],dp[i-1][2]);
                    if(dp[i-1][1]>dp[i-1][2])
                        pre[i][j]=2;
                    else
                        pre[i][j]=1;
                }
            }
        }

        int minn=min(dp[n+1][1],min(dp[n+1][2],dp[n+1][3])); //找到最小值
        if(minn==dp[n+1][1])
            fact(n+1,1);
        else if(minn==dp[n+1][2])
            fact(n+1,2);
        else
            fact(n+1,3);
        printf("Case %d: %d ",++cas,sum);
        for(i=1;i<=n;i++)
            printf("%c",ans[i]+A-1);
        printf("\n");
      //  test(n);
    }

    return 0;
}
View Code

【動態規劃】ZZNU-OJ- 2054 : 油田