VIJOS1240 樸素的網路遊戲[DP]

Candy?發表於2016-08-24

描述

佳佳最近又迷上了某款類似於虛擬人生的網路遊戲。在遊戲中,佳佳是某旅行團的團長,他需要安排客戶住進旅館。旅館給了佳佳的旅行團一個房間數的限制。每一個房間有不同的容納人數和價錢(這個價格是房間的總價格,不是每個人付的)。佳佳決定找到最小的花費,安排參加旅行的人住在這裡。但是他遇到了這麼一個問題:兩個不同性別的人不能住在同一個房間裡,除非他們是夫妻;一對夫妻如果在一起住,那麼別的人就不能再住進去。你不必讓所有的夫妻都單獨住在一起。也就是說:
1.給你一些房間,告訴你這些房間的容納人數和價格
2.安排一定數量的人住到旅館裡,滿足:
a.不同性別的人如果不是夫妻那麼不能住一起。
b.夫妻如果住在一起,那麼房間不能安排其他的人進去。

你來寫一個程式幫助佳佳找到安排這些來參加旅行的人住進旅館所需要的最小花費。

格式

輸入格式

第一行有4個用空格隔開的整數m,f,r,c,分別表示參加旅行的男性人數、參加旅行的女性人數、旅館的房間數、這些男女中有多少對夫妻。注意每一個人不是單身就是和他/她唯一的妻子/丈夫一起參加旅行。

接下來有r行,每行描述了一個房間。每行有兩個整數Bi,Pi,它們分別表示每一個房子的容納人數和價格(無論住多少人,房間的價格不變)。

對於30%的資料,0<=m,f,r<=50;
對於100%的資料,0<=m,f,r<=300,0<=c<=Min(m,f),0<=Bi,Pi<=10。

輸出格式

輸出為旅行的人訂購房間所需要的最小花費。如果沒有這樣的安排,請輸出“Impossible”代替。

樣例1

樣例輸入1[複製]

2 1 3 1
3 5
2 10
2 4

樣例輸出1[複製]

 
9

限制

各測試點5秒

-----------------------------------------

總存在一個最優解使得只有一對couple在一起或者沒有,因為假如有兩隊,可以男女交換

f[i][j][k]和g[i][j][k]分別表示沒有和有一對,轉移時除了f向f,g向g外,再考慮g[i][j][k]=min(g[i][j][k],f[i-1][j-1][k-1]+p[i])

【WARN】:1.注意這個轉移g[i][j][k]=min(g[i][j][k],f[i-1][j-1][k-1]+p[i]) j和k>=1

2.房間不一定注滿人,如果再加一層列舉房間住幾個人會很慢,可以把j和k<0的情況從不合法改成從0轉移,想當於考慮了房間不滿,最多男女各有一個房間不滿

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=305,INF=1e9;
int m,fm,r,c,b[N],p[N];
int f[N][N][N],g[N][N][N];

void dp(){
    for(int j=0;j<=m;j++) for(int k=0;k<=fm;k++) f[0][j][k]=g[0][j][k]=INF;
    f[0][0][0]=0;
    for(int i=1;i<=r;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<=fm;k++){
                f[i][j][k]=f[i-1][j][k]; g[i][j][k]=g[i-1][j][k];
                if(b[i]>=2&&j>=1&&k>=1) g[i][j][k]=min(g[i][j][k],f[i-1][j-1][k-1]+p[i]);                
                    int sj=max(0,j-b[i]),sk=max(0,k-b[i]);
                    f[i][j][k]=min(f[i][j][k],f[i-1][sj][k]+p[i]),
                            g[i][j][k]=min(g[i][j][k],g[i-1][sj][k]+p[i]);
                    f[i][j][k]=min(f[i][j][k],f[i-1][j][sk]+p[i]),
                            g[i][j][k]=min(g[i][j][k],g[i-1][j][sk]+p[i]);
            }
}
int main(){
    scanf("%d%d%d%d",&m,&fm,&r,&c);
    for(int i=1;i<=r;i++) scanf("%d%d",&b[i],&p[i]);
    dp();
    
    int ans;
    if(c==0) ans=f[r][m][fm];
    else ans=min(f[r][m][fm],g[r][m][fm]);
    if(ans!=INF) cout<<ans;
    else cout<<"Impossible"; 
}
//題解中神犇的滾動陣列做法 邊讀邊做
//快了近三倍
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 300 +  24;
int m,f,r,c;
int dp[N][N][2];
int main()
{
    scanf("%d%d%d%d",&m,&f,&r,&c);
    
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[m][f][1] = dp[m][f][0] = 0;
        for(int i = 0; i < r; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            for(int i = 0; i <= m; i++)
            {
                for(int j = 0; j <= f; j++)
                {
                    int x1 = max(0,i-a);
                    int x2 = max(0,j-a);
                    dp[x1][j][0] = min(dp[x1][j][0],dp[i][j][0] + b);
                    dp[i][x2][0] = min(dp[i][x2][0],dp[i][j][0] + b);
                    dp[x1][j][1] = min(dp[x1][j][1],dp[i][j][1] + b);
                    dp[i][x2][1] = min(dp[i][x2][1],dp[i][j][1] + b);
                    if(i > 0 && j > 0 && a >= 2)
                        dp[i-1][j-1][1] = min(dp[i-1][j-1][1],dp[i][j][0] + b);
                }
            }
        }
        int d = min(dp[0][0][0],dp[0][0][1]);
        if(d == 0x3f3f3f3f)
            printf("Impossible");
        else
        printf("%d",d);
    
}

 

相關文章