Codeforces Round #385 (Div. 1) C. Hongcow Buys a Deck of Cards(DP/模拟退火)

185 篇文章 0 订阅
116 篇文章 0 订阅

One day, Hongcow goes to the store and sees a brand new deck of n special cards. Each individual card is either red or blue. He decides he wants to buy them immediately. To do this, he needs to play a game with the owner of the store.

This game takes some number of turns to complete. On a turn, Hongcow may do one of two things:

  • Collect tokens. Hongcow collects 1 red token and 1 blue token by choosing this option (thus, 2 tokens in total per one operation).
  • Buy a card. Hongcow chooses some card and spends tokens to purchase it as specified below.

The i-th card requires ri red resources and bi blue resources. Suppose Hongcow currently has A red cards and B blue cards. Then, thei-th card will require Hongcow to spend max(ri - A, 0) red tokens, and max(bi - B, 0) blue tokens. Note, only tokens disappear, but the cards stay with Hongcow forever. Each card can be bought only once.

Given a description of the cards and their costs determine the minimum number of turns Hongcow needs to purchase all cards.

Input

The first line of input will contain a single integer n (1 ≤ n ≤ 16).

The next n lines of input will contain three tokens ciri and bici will be 'R' or 'B', denoting the color of the card as red or blue. ri will be an integer denoting the amount of red resources required to obtain the card, and bi will be an integer denoting the amount of blue resources required to obtain the card (0 ≤ ri, bi ≤ 107).

Output

Output a single integer, denoting the minimum number of turns needed to acquire all the cards.

Examples
input
3
R 0 1
B 1 0
R 1 1
output
4
input
3
R 3 0
R 2 0
R 1 0
output
6
Note

For the first sample, Hongcow's four moves are as follows:

  1. Collect tokens
  2. Buy card 1
  3. Buy card 2
  4. Buy card 3
Note, at the fourth step, Hongcow is able to buy card  3  because Hongcow already has one red and one blue card, so we don't need to collect tokens.

For the second sample, one optimal strategy is as follows:

  1. Collect tokens
  2. Collect tokens
  3. Buy card 2
  4. Collect tokens
  5. Buy card 3
  6. Buy card 1

At the fifth step, even though Hongcow has a red token, Hongcow doesn't actually need to spend it, since Hongcow has a red card already.

题意:有n张卡片需要你收集,每种卡片或为红色或为蓝色,想要获得第i张卡片需要话费 max(ri - A, 0) 红 tokens和 max(bi - B, 0) 蓝 tokens,A和B是你现在持有的两种卡片的数量,每一步你可以选择买一张卡片或者获得红蓝各一个token,问你最少需要多少轮可以得到所有的卡片。


分析:n <= 16,很小,可以考虑状压DP或者模拟退火。


模拟退火做法很暴力,每次随机调整购买顺序,我把初始温度调到了max*n就通过了所有测试数据:


#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#define INF 2147483640
#define eps 1e-9
#define N 20
#define MOD 998244353
using namespace std;
int n,Max,ans,r[N],a[N],temp[N],b[N];
string op[N];
int value(const int a[])
{
	int buy = 0,la = 0,lb = 0,na = 0,nb = 0;
	for(int i = 1;i <= n;i++)
	{
		int v = a[i];
		if(max(0,r[v]-na) > la) 
		{
			buy += max(0,r[v]-na) - la;
			lb += max(0,r[v]-na) - la;
			la += max(0,r[v]-na) - la;
		}
		if(max(0,b[v]-nb) > lb)
		{
			buy += max(0,b[v]-nb) - lb;
			la += max(0,b[v]-nb) - lb;
			lb += max(0,b[v]-nb) - lb;
		}
		la -= max(0,r[v]-na),lb -= max(0,b[v]-nb);
		if(op[v][0] == 'R') na++;
		else nb++;
	}
	return buy;
}
int main()
{
	cin>>n;
	for(int i = 1;i <= n;i++) 
	{
		cin>>op[i]>>r[i]>>b[i];
		Max = max(Max,r[i]);
		Max = max(Max,b[i]);	
	}
	for(int i = 1;i <= n;i++) a[i] = i;
	int time = 0;
	double t = Max*n;
	while(t > eps)
	{
		int l = rand() % n + 1,r = rand() % n + 1;
		for(int i = 1;i <= n;i++) temp[i] = a[i];
		swap(temp[l],temp[r]);
		double de = value(a) - value(temp);
		if(de > 0 || exp(de/t) > (rand()%1000)*0.001) swap(a[l],a[r]);
		t = t*0.9999;
		time++;
	}
	cout<<value(a)+n<<endl;
}


然后是我自己的DP版本,写的很丑:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define N 16
#define MOD 998244353
using namespace std;
int n,ans,r[1<<N],b[1<<N],tot_r[1<<N],tot_b[1<<N],cost_r[1<<N],cost_b[1<<N];
char op[1<<N];
int f[1<<16][2][16*16+1];
int lowbit(int x)
{
	return x & (-x);
}
void update(int &x,int y)
{
	x = min(x,y);
}
int main()
{
	cin>>n;
	for(int i = 0;i < n;i++) cin>>op[1<<i]>>r[1<<i]>>b[1<<i];
	for(int i = 1;i < (1<<n);i++)
	{
		tot_r[i] = tot_r[i-lowbit(i)];
		tot_b[i] = tot_b[i-lowbit(i)];
		if(op[lowbit(i)] == 'B') tot_b[i]++;
		else tot_r[i]++;
		cost_r[i] = cost_r[i-lowbit(i)] + r[lowbit(i)];
		cost_b[i] = cost_b[i-lowbit(i)] + b[lowbit(i)];
	}
	memset(f,INF,sizeof(f));
	f[0][0][0] = f[0][1][0] = 0;
	ans = INF;
	for(int i = 0;i < (1<<n);i++)
	 for(int j = 0;j <= 1;j++)
	  for(int res = 0;res <= n*n;res++)
	  {
	  	 if(f[i][j][res] >= INF) continue;
	  	 if(i == (1<<n)-1) update(ans,f[i][j][res]);
	     int sumr = 0;
	   	 int sumb = f[i][j][res] - (cost_b[i] - res);
	   	 if(j)
	   	 {
	   	 	 sumb = 0;
			 sumr = f[i][j][res] - (cost_r[i] - res);
		 }
	  	 for(int k = 1;k <= n;k++)
	      if(!(i & (1<<(k-1))))
	      {
	      	 int cr = max(r[1<<(k-1)]-tot_r[i],0),cb = max(b[1<<(k-1)]-tot_b[i],0);
	      	 int nr = sumr - cr,nb = sumb - cb;
	      	 int val = f[i][j][res];
	      	 if(nr < 0)
	      	 {
	      	 	val += -nr;
	      	 	nb += -nr;
	      	 	nr += -nr;
			 }
			 if(nb < 0)
			 {
			 	val += -nb;
			 	nr += -nb;
			 	nb += -nb; 
			 }
			 if(!nr)  update(f[i+(1<<(k-1))][0][b[1<<(k-1)]-cb+ (!j ? res : (cost_b[i] - f[i][j][res]))],val);
			 if(!nb)  update(f[i+(1<<(k-1))][1][r[1<<(k-1)]-cr+ (j ? res : (cost_r[i] - f[i][j][res]))],val);
	      }
	  }
   cout<<ans+n<<endl;
} 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值