http://poj.org/problem?id=2724
题意:
给了n,m。表示n个开关,m个操作。如果当前开关为’*’表示同时匹配0,1。只能将这m个操作所洗过的再洗一次,试问最少进行多少次操作。
思路:
要进行最少的操作肯定是优先进行’*’操作,所以就将这个转换成普通的0,1。所有的操作中,如果有只相差一位的就连一条边,最后将问题转换成最多有多少个两两不相连的点,也就是最大的独立集。
做法:
两种做法,一个vector存所有的string,并用map存每个string出现的次数。遍历vector,再遍历当前string,依次改变每一位,看map中是否有对应的出现。O(V*len)
另一种做法。
将每个string对应成一个数字,对于每一个二进制数字,在vector中查找是否有跟他只相差一位的二进制数字。O(V^2)
判断是否只差一位可以用位运算。
int c = a^b;
int tmp = c && (c&(c-1));
if(tmp == 0) //说明只相差一位
但是结果发现第一种方法反而慢了一点。。因为常数?
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
#define M 10009
int n,m;
int match[M];
bool used[M];
map<string,int> mp;
vector<int> G[M];
vector<string> ss;
int V;
void init()
{
ss.clear();
mp.clear();
for(int i = 0;i < M;i++) G[i].clear();
}
void add_edge(int u,int v)
{
G[u].push_back(v);
G[v].push_back(u);
}
bool dfs(int v)
{
used[v] = true;
for(int i = 0;i < G[v].size();i++)
{
int u = G[v][i], w = match[u];
if(w < 0 || !used[w] && dfs(w))
{
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int b_match()
{
int res = 0;
memset(match,-1,sizeof(match));
for(int i = 0; i < V;i++)
{
if(match[i] < 0)
{
memset(used,0,sizeof(used));
if(dfs(i)) res++;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> n >> m)
{
init();
if(n == 0 && m == 0) break;
int k = 1;
ss.push_back("1");
for(int i = 0;i < m;i++)
{
string tmp;
cin >> tmp;
int len = tmp.length();
bool ok = false;
for(int i = 0;i < len;i++)
{
if(tmp[i] == '*')
{
ok = true;
tmp[i] = '0';
if(mp[tmp] == 0)
{
mp[tmp] = k++;
ss.push_back(tmp);
}
tmp[i] = '1';
if(mp[tmp] == 0)
{
mp[tmp] = k++;
ss.push_back(tmp);
}
break;
}
}
if(!ok)
{
if(mp[tmp] == 0)
{
mp[tmp] = k++;
ss.push_back(tmp);
}
}
}
V = ss.size();
for(int i = 1;i < V;i++)
{
string s1 = ss[i];
int len = s1.length();
for(int j = 0;j < len; j++)
{
if(s1[j] == '1')
{
s1[j] = '0';
if(mp[s1])
add_edge(i,mp[s1]);
s1[j] = '1';
}
if(s1[j] == '0')
{
s1[j] = '1';
if(mp[s1]) add_edge(i,mp[s1]);
s1[j] = '0';
}
}
}
printf("%d\n",V-1-b_match());
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
#define M 10009
int n,m;
int match[M];
bool used[M];
bool vis[M];
vector<int> G[M];
vector<int> ss;
int V;
void init()
{
ss.clear();
memset(vis,false,sizeof(vis));
for(int i = 0;i < M;i++) G[i].clear();
}
void add_edge(int u,int v)
{
G[u].push_back(v);
G[v].push_back(u);
}
bool dfs(int v)
{
used[v] = true;
for(int i = 0;i < G[v].size();i++)
{
int u = G[v][i], w = match[u];
if(w < 0 || !used[w] && dfs(w))
{
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int b_match()
{
int res = 0;
memset(match,-1,sizeof(match));
for(int i = 0; i < V;i++)
{
if(match[i] < 0)
{
memset(used,0,sizeof(used));
if(dfs(i)) res++;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> n >> m)
{
init();
if(n == 0 && m == 0) break;
for(int i = 0;i < m;i++)
{
string tmp;
cin >> tmp;
int len = tmp.length();
bool ok = false;
int index = -1;
int suma = 0,sumb = 0;
for(int i = 0;i < len;i++)
{
if(tmp[i] == '*')
{
index = i;
suma = suma*2 + 1;
sumb = sumb*2 + 0;
}
else
{
suma = suma*2+tmp[i]-'0';
sumb = sumb*2+tmp[i]-'0';
}
}
if(index == -1)
{
if(!vis[suma])
{
vis[suma] = true;
ss.push_back(suma);
}
}
else
{
if(!vis[suma])
{
vis[suma] = true;
tmp[index] = '1';
ss.push_back(suma);
}
if(!vis[sumb])
{
vis[sumb] = true;
tmp[index] = '0';
ss.push_back(sumb);
}
}
}
V = ss.size();
for(int i = 0;i < V;i++)
{
for(int j = i + 1;j < V;j++)
{
int a = ss[i];
int b = ss[j];
int c = a^b;
int tmp = c && (c&(c-1));
if(tmp == 0) add_edge(i,j);
}
}
cout << V - b_match() << endl;
}
return 0;
}
本文探讨了如何通过最少的操作数解决给定开关匹配问题,利用优先进行'*'操作将其转换为普通0,1操作,并通过寻找最大独立集来解决。介绍了两种实现方法,一种是使用字符串和哈希表,另一种则是将每个字符串转化为二进制数字并查找相差一位的数字。最终通过求解最大独立集得到最少操作数。
3924

被折叠的 条评论
为什么被折叠?



