Water Gate Management - 子集生成

Problem Description
A dam has n water gates to let out water when necessary. Each water gate has its own capacity, water path and affected areas in the downstream. The affected areas may have a risk of flood when the water gate is open. The cost of potential damage caused by a water gate is measured in number calculated from the number of people and areas estimated to get affected. Suppose a water gate Gi has the volumetric flow rate of Fi m3/hour and the damage cost of Ci. In a certain situation, the dam has the volume V m3 of water to flush out within T hours. Your task is to manage the opening of the water gates in order to get rid of at least the specified volume of water within a limited time in condition that the damage cost is minimized.
For example, a dam has 4 water gates and their properties are shown in the following table.
这里写图片描述
Case 1: You have to flush out the water 5 million m3 within 7 hours. The minimum cost will be 120,000 by letting the water gate G1 open for 7 hours.
Case 2: You have to flush out the water 5 million m3 within 30 hours. The minimum cost will be 110,000 by letting the water gates G2 and G3 open, for example, G2 is open for 29 hours and G3 is open for 28 hours.
Note that each water gate is independent and it can be open only in a unit of whole hour (no fraction of hour).
Input
The first line includes an integer n indicating number of water gates (1 ≤ n ≤ 20). Then the next n lines contain, in each line, two integers: Fi and Ci corresponding to the flow rate (m3/hour) and the damage cost of the water gate Gi respectively. The next line contains the number m which is the number of test cases (1 ≤ m ≤ 50). The following m lines contain, in each line, two integers: V and T corresponding to the volume (m3) of water to let out within T hours. (1 ≤ Fi,V,Ci ≤ 109, 1 ≤ T ≤ 1000)
Output
For each test case, print out the minimum cost in the exact format shown in the sample output below. If it is not possible to let out the water of volume V in T hours from the dam, print out ‘IMPOSSIBLE’ (without quotation marks).
Sample Input
4
720000 120000
50000 60000
130000 50000
1200000 150000
3
5000000 7
5000000 30
63000000 24
Sample Output
Case 1:
120000
Case 2:
110000
Case 3:
IMPOSSIBLE
题意
水坝有很多闸门,已知闸门每小时流水速度和闸门的花费。现在给你体积V的水,你需要在T小时内泄洪,求它最好的搭配,可打开多个闸门。
思路
暴力法,生成闸门搭配的所有解决方案,然后逐一比较答案。
二进制生成所有子集算法
这里写图片描述

#include<iostream>
#include<cstdio>
using namespace std;
void sub_set(int n,int s)
{
#include<iostream>
#include<cstdio>
using namespace std;
void sub_set(int n,int s)
{
    for(int i=0;i<n;i++)
        if(s&(1<<i)) printf("%d",i);
    printf("\n");
}
int main()
{
    int n;
    printf("求1-n的所有子集,n=");
    scanf("%d",&n);
    for(int i=1;i<(1<<n);i++)
        sub_set(n,i);
    return 0;
}

解题代码
这里写图片描述

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
const LL INF=1e18;
const int MAX=1048576;//2^20,最大子集个数;
struct gate
{
    LL v;//体积
    LL c;//花费
}g[22],sum[MAX];//g为所有闸门数据,s为组合到的闸门方案
int sub_set(int n)//枚举1-n的个排列,共2^n-1个子集,返回子集个数
{
    int cnt=1<<n;
    for(int i=1;i<cnt;i++){
        for(int j=0;j<n;j++){
            if(i&(1<<j)){
                sum[i].v+=g[j].v;
                sum[i].c+=g[j].c;
            }
        }
    }
    return cnt;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lld%lld",&g[i].v,&g[i].c);
    int cnt=sub_set(n);
    //printf("%d\n",cnt);
    int m;
    scanf("%d",&m);
    for(int cse=1;cse<=m;cse++)
    {
        LL v,t;
        scanf("%lld%lld",&v,&t);//输入目标体积与时间
        LL minn=INF;//此处无限大.
        for(int i=0;i<cnt;i++)
        {
            if(sum[i].v*t>=v &&sum[i].c<minn){
                minn=sum[i].c;
            }
        }
        printf("Case %d: ",cse);
        if(minn!=INF){
            printf("%lld\n",minn);
        }else{
            printf("IMPOSSIBLE\n");
        }
    }
    return 0;
}

另附一位学长提供代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
typedef long long ll;
const int MAXN = 1e2 + 10;
const ll INF = 1e18;

using namespace std;
int n;
ll t, v, ans;
ll a[MAXN], b[MAXN];

void dfs(int x, int s)
{
    if(x == n)
    {
        int tt;
        ll cost = 0;
        ll V = 0;
        int cnt = 0;
        while(s)
        {
            tt = (s & 1);
            if(tt)
            {
                cost += b[cnt];
                V += t * a[cnt];
            }
            cnt++;
            s /= 2;
        }
        if(V >= v) ans = min(ans, cost);
        return;
    }
    dfs(x + 1, s * 2);
    dfs(x + 1, s * 2 + 1);
}

int main()
{
    while(~scanf("%d", &n))
    {
        for(int i = 0; i < n; i++)
        {
            scanf("%lld%lld", &a[i], &b[i]);
        }
        int m;
        scanf("%d", &m);
        for(int c = 1; c <= m; c++)
        {
            ans = INF;
            scanf("%lld%lld", &v, &t);
            dfs(0, 0);
            dfs(0, 1);
            printf("Case %d: ", c);
            if(ans == INF) printf("IMPOSSIBLE\n");
            else printf("%lld\n", ans);
        }
    }
    return 0;
}

练习网址
https://vjudge.net/problem/UVALive-5723

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值