Tanks CodeForces - 920D(DP)

Petya sometimes has to water his field. To water the field, Petya needs a tank with exactly V ml of water.

Petya has got N tanks, i-th of them initially containing ai ml of water. The tanks are really large, any of them can contain any amount of water (no matter how large this amount is).

Also Petya has got a scoop that can contain up to K ml of water (initially the scoop is empty). This scoop can be used to get some water from some tank, and after that pour it all into some tank (it is impossible to get water from multiple tanks without pouring it, or leave some water in the scoop when pouring it). When Petya tries to get some water from a tank, he gets min(v, K) water, where v is the current volume of water in the tank.

Is it possible to obtain a tank with exactly V ml of water using these operations? If it is possible, print a sequence of operations that allows to do it. If there are multiple ways to obtain needed amount of water in some tank, print any of them.

Input
The first line contains 3 integers: N (2 ≤ N ≤ 5000), K (1 ≤ K ≤ 5000), and V (0 ≤ V ≤ 109) — the number of tanks, the maximum volume of water the scoop can contain, and the required amount of water in some tank, respectively.

The second line contains N integers ai (0 ≤ ai ≤ 105), where ai is initial volume of water in i-th tank.

Output
If it is impossible to obtain a tank with exactly V ml of water, print NO.

Otherwise print YES in the first line, and beginning from the second line, print the sequence of operations in the following format:

Each line has to contain 3 numbers denoting a compressed operation: “cnt x y” (1 ≤ cnt ≤ 109, 1 ≤ x, y ≤ N), where x is the index of the tank where we get water, y is the index of the tank where we pour water, and cnt is the number of times we transfer water from tank x to tank y.

The number of these lines must not exceed N + 5.

Examples
Input
2 3 5
2 3
Output
YES
1 2 1
Input
2 3 4
2 3
Output
NO
Input
5 2 0
1 3 5 7 9
Output
YES
2 2 1
3 3 1
4 4 1
5 5 1

思路来源:https://www.cnblogs.com/kkkkahlua/p/8413054.html
题意:
你有n个水池,有一定量的水且容积无限大。一个容积为k的勺子,可以装一个水池的min(k,a[i])的水放到其他水池。求能否配出v容积的水。
思路:
首先把数字都转换为k的模数来算。
定义F[i][j]为前i个水池配出j水量的可行性。
F[i][j] =
0 ——不可行
1 ——可行但不必要用a[i]
2 ——可行且一定要用a[i]

那么由此可以知道能否配出v容积,并且由结果反推得出哪些是一定要用的,哪些是不一定要用的。

如果sum{a[i]]} 或者 dp[n][v%k] =0,那么肯定不行。

如果没有一定要用的,那么v一定是k的倍数,将所有水集中起来,再放v/k个勺子的水出来就好了
否则的话就把所有一定要用的水集中起来为s1,不一定要用的水集中起来为s2。那么s1与v的差距一定为k的倍数,加上(减去)这个倍数就好了。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int a[5005],b[5005],dp[5005][5005];
int flag[5005];

int main()
{
    int n,k,v;scanf("%d%d%d",&n,&k,&v);
    int sum = 0;
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
        b[i] = a[i];a[i] %= k;
        sum += b[i];
    }
    
    if(sum < v)
    {
        printf("NO\n");
        return 0;
    }
    dp[0][0] = 1;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 0;j < k;j++)
        {
            if(dp[i - 1][j])
            {
                dp[i][j] = 1;
                if(!dp[i][(j + a[i]) % k])dp[i][(j + a[i]) % k] = 2;
            }
        }
    }
    int num = v % k;
    if(!dp[n][num])
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    
    int x1 = -1,x2 = -1,ans = 0;
    
    for(int i = n;i >= 1;i--)
    {
        if(dp[i][num] == 2)
        {
            flag[i] = true;
            num = (num - a[i] + k) % k;
            ans += b[i];
            if(x1 == -1)x1 = i;
        }
        else if(x2 == -1)x2 = i;
    }
    if(x1 == -1)//没有一定要添加的,那就是添加任何一个都可以,那v一定是k的倍数
    {
        for(int i = 1;i < n;i++)
        {
            if(b[i])
            {
                printf("%d %d %d\n",(b[i] + k - 1) / k,i,n);
            }
        }
        if(v / k)printf("%d %d %d\n",v / k,n,1);
        return 0;
    }
    
    int res = v - ans;
    int s1 = b[x1],s2 = b[x2];
    for(int i = 1;i <= n;i++)
    {
        if(i == x1 || i == x2 || !b[i])continue;
        if(!flag[i])
        {
            printf("%d %d %d\n",(b[i] + k - 1) / k,i,x2);//非必要的全部放到x2
            s2 += b[i];
        }
        else
        {
            printf("%d %d %d\n",(b[i] + k - 1) / k,i,x1);//必要的全部放到x1
            s1 += b[i];
        }
    }
    int cnt = (v - s1) / k;
    if(cnt > 0)printf("%d %d %d\n",cnt,x2,x1);//此时x1与v的差一定是k的倍数
    else if(cnt < 0)printf("%d %d %d\n",-cnt,x1,x1 == 1 ? 2 : 1);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值