UVA - 1252 Gym - 100676G (状压dp)

10 篇文章 0 订阅
5 篇文章 0 订阅

题意:给你n个长m的不相同的0,1字符串,代表n个人对m个问题的不同回答,问你至少问几个问题可以知道是哪一个人。

dp[i][j]表示问题串为i且答案为j时能否区分所有人。

 

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
#define  INF 0x3f3f3f3f

int f[(1<<11)+10][(1<<11)+10];
int p[1234];
int n,m;
int dfs(int s1,int s2)	//问题为s1答案为s2 
{
	if(f[s1][s2]!=INF) return f[s1][s2];
	int cnt=0;
	for(int i=0;i<m;i++)
	{
		if((p[i]&s1)==s2)
		cnt++;
	}
	if(cnt<2) return f[s1][s2]=0;
	
	for(int i=0;i<n;i++)
	{
		if((s1&(1<<i))==0)
		{
			f[s1][s2]=min(f[s1][s2],max(dfs(s1^(1<<i),s2),dfs(s1^(1<<i),(s2^(1<<i))))+1);
		}
	}
	return f[s1][s2];
}
int main()
{
	while(cin>>n>>m)
	{
		if(!n&&!m)break;
		string str;
		memset(f,INF,sizeof(f));
		memset(p,0,sizeof(p));
		for(int i=0;i<m;i++)
		{
			cin>>str;
			for(int j=0;j<str.size();j++)
			{
				if(str[j]=='1')p[i]|=(1<<j);
			}
		}
		cout<<dfs(0,0)<<endl;
	}
	
	return 0;
}

 

Gym - 100676G

题意:n个课程,每天学习一个,在第t天学习第w[i]门课程他会获得t * w[i]的价值,然后这些课程有先后顺序,一个课程有先修课程,必须学完先修才能学这门课程,问你能得到的最大价值。

思路:虽然预先知道这道题是状压dp,但刚看到这题想到了拓扑排序感觉可行,然后wa到弃疗。因为n很小,我们可以直接状态当前已经修过的课程,然后枚举下一个可以修的课程,更新添加了这个课程之后所取得最大值。因为最后要修完所有的课程所以直接输出dp[(1<<n)-1]即可。map字符串处理的方式真是太强了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll maxn = 200;
const int INF = 1e8 + 5;
int w[maxn],dp[1<<21];
int n, m, pr[25];
bool vis[1<<21]; 
map<string, int> mp;
int main() 
{
    int t;
	scanf("%d",&t);
    while (t--) 
	{    
		mp.clear();
		scanf("%d%d",&n,&m);
	    memset(pr, 0, sizeof(pr));
		cin.get();
	    for (int k = 0; k < n; k++) 
		{
	        string s, name; int xx = 0;
	        getline(cin, s); int flag = 1;
	        for (int i = 0; i < s.size(); i++) 
			{
	            if (flag && s[i+1] >= '0' && s[i+1] <= '9') 
				{
	                flag = 0; continue;
	            }
	            if (flag) name += s[i];
	            if (!flag) xx = xx * 10 + s[i] - '0';
	        }
	        mp[name] = k; w[k] = xx;
	    }
	    for (int i = 1; i <= m; i++) {
	        string s, xx, hh;
	        getline(cin, s); int flag = 0;
	        for (int j = 0; j < s.size(); j++) {
	            if (s[j+1] == '-') {
	                flag = 1; continue;
	            }
	            if (flag == 1 && s[j] == ' ') {
	                flag = 2; continue;
	            }
	            if (flag == 0) xx += s[j];
	            if (flag == 2) hh += s[j];
	        }
	       pr[mp[hh]] |= (1 << mp[xx]); //如果要学习第mp[hh]课程的话要先学习mp[xx]课程
	    }
	    for(int i=1;i<(1<<n);i++)
	    dp[i] = -INF;
	    dp[0] = 0;
	    vis[0] = 1;
	    for (int i=0;i<(1<<n);i++)
		{
	        if (!vis[i]) continue;
	        int num=0;
	        for(int j=0;j<n;j++) 
			{
	            num+=((i>>j)&1);
	        }
	        for(int j=0;j<n;j++)
			{
	            int xx=i|(1<<j);
	            if(xx!=i) 
				{
	                if((i&pr[j])!=pr[j]) continue;
	                vis[xx]=1;
	                dp[xx]=max(dp[xx],dp[i]+(num+1)*w[j]);
	            }
	        }
	    }
	    printf("%d\n",dp[(1<<n)-1]);
    }
    return 0;
}

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值