写题记录2

记录一些不太会的题目

1

Online Judge

原题:UVA658

这题我们首先想到是先状压,再建图,求个最短路,但是会发现建图的话最坏情况点能达到2n2^n级别,这空间可能过不去。于是我们转换成类BFS+SPFA的方式,即每次更新节点的时候都把可能的边转换遍历一次(反正最多才100),再在这个所谓的隐式图上跑个SPFA就好了。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef double dd;
const int N = 2e6+5;
const dd eps = 1e-8;
const int mod = 1e9+7;

int n,m,cost[120],vis[N],dis[N];
string op[120],to[120];
queue<int> qu;

bool check(int now,string s){
  for(int i = 0;i < s.size();i++){
    int sta = (now&(1<<i));
    if(s[i] == '-' and sta) return false;
    if(s[i] == '+' and !sta) return false;
  }
  return true;
}

int change(int now,string s){
  int ans = 0;
  for(int i = 0;i < s.size();i++){
    int old = now&(1<<i);
    if(s[i] == '0' and old) ans += (1<<i);
    if(s[i] == '+') ans += (1<<i);
  }
  return ans;
}

void spfa(int st){
  memset(vis,0,sizeof(vis));
  for(int i = 0;i < (1<<n);i++) dis[i] = 1e9;
  dis[st] = 0;
  qu.push(st);vis[st] = 1;
  while(!qu.empty()){
    int now = qu.front();vis[now] ^= 1;qu.pop();
    for(int i = 1;i <= m;i++){
      if(check(now,op[i])){
			int nex = change(now,to[i]);
			if(dis[nex] > dis[now]+cost[i]){
			  dis[nex] = dis[now] + cost[i];
			  if(!vis[nex]) qu.push(nex),vis[nex] = 1;
				}
      }
    }
  }
}

int main(){
  // freopen("input.txt","r",stdin);
  int kase = 1;
  while(cin>>n>>m and n){
    for(int i = 1;i <= m;i++){
      cin>>cost[i]>>op[i]>>to[i];
    }
    spfa((1<<n)-1);
    printf("Product %d\n",kase++);
    if(dis[0] == 1e9) cout<<"Bugs cannot be fixed."<<endl;
    else printf("Fastest sequence takes %d seconds.\n",dis[0]);
    cout<<endl;
  }
  return 0;
}

2

Online Judge

题意:给了36张牌,分成9堆,每次都可以取两张位于堆顶且大小相同的牌,问取到一张不剩的概率

发现自己的概率还停留在高中的sb思维。。。只会无脑求出符合题意的事件次数除以总事件数,然而在这里肯定不行,复杂度太大。

于是我们就得转换一下思路,我这里用了字符串表示状态套一层unordered_map当数组用,用dp(sta)dp(sta)表示在sta下满足题意的概率,那么对于从状态s1s2s_1\rightarrow s_2,令p为转移到s2s_2的概率,那么显然s2s_2dp(s1)dp(s_1)的贡献就为dp(s2)×pdp(s_2)\times p,再写一下记忆化搜索就好了。

同时还要注意一下输入的问题,题目要求连续输入多个数据且中间没有间隔,学到了一个方法就是写一个bool函数,同时也注意到cin>>这个表达式也是有返回值的。。。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef double dd;
const int N = 1e6+5;
const dd eps = 1e-8;
const int mod = 1e9+7;

stack<int> qu[20];
string ca;
dd ans;
unordered_map<string,dd> vis;
int n,fac[100];

dd dfs(string sta){
  if(sta == "000000000"){return 1.0;}
  if(vis.count(sta)) return vis[sta];
  int cnt = 0;
  for(int i = 1;i <= 9;i++){
    if(qu[i].empty()) continue;
    for(int j = i+1;j <= 9;j++){
      if(qu[j].empty()) continue;
      if(qu[i].top() == qu[j].top()) cnt++;
    }
  }
  if(cnt == 0) return 0.0;
  for(int i = 1;i <= 9;i++){
    if(qu[i].empty()) continue;
    for(int j = i+1;j <= 9;j++){
      if(qu[j].empty()) continue;
      int a = qu[i].top(),b = qu[j].top();
      if(a == b){
	qu[i].pop();qu[j].pop();
	string nex = sta;
	nex[i-1] = sta[i-1]-1;
	nex[j-1] = sta[j-1]-1;
	vis[sta] += dfs(nex)/dd(cnt);
	qu[i].push(a);qu[j].push(b);
      }
    }
  }
  return vis[sta];
}

bool read(){
  for(int i = 1;i <= 9;i++){
    while(!qu[i].empty()) qu[i].pop();
  }
  for(int i = 1;i <= 9;i++){
    for(int j = 1;j <= 4;j++){
      if(!(cin>>ca)) return false;
      if(ca[0] == 'J') qu[i].push(11);
      else if(ca[0] == 'Q') qu[i].push(12);
      else if(ca[0] == 'K') qu[i].push(13);
      else if(ca[0] == 'A') qu[i].push(14);
      else if(ca[0] == 'T') qu[i].push(10);
      else {qu[i].push(ca[0]-'0');}      
    }
  }
  return true;
}

int main(){
  // freopen("input.txt","r",stdin);
  while(read()){
    vis.clear();
    printf("%.6f\n",dfs("444444444"));    
  }
  return 0;
}

3

原题:UVA1660

题意:给出n个点的无向图(n50)(n\le50),求最少删除多少个点,使整张图不联通

我们可以先选出两个点s,ts,t,整张图不联通只需要使s,ts,t不联通,等价于一个二分图划分,我们把其他点拆点,再连边,最后跑个Dinic求最小割后取最小值即可。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef double dd;
const int N = 1e5+5;
const dd eps = 1e-8;
const int mod = 1e9+7;

string pa;
int head[N],n,m,s,t,vis[N],d[N],cur[N],cnt=1,u,v,a[10000],b[10000];
ll w;
struct node{
  int to,nex;
  ll cap,flow;
}edge[N];

void add_edge(int u,int v,ll w){
  edge[++cnt].to = v;
  edge[cnt].cap = w;
  edge[cnt].flow = 0;
  edge[cnt].nex = head[u];
  head[u] = cnt;
  edge[++cnt].to = u;
  edge[cnt].cap = 0;
  edge[cnt].flow = 0;
  edge[cnt].nex = head[v];
  head[v] = cnt;
}

bool bfs(){
  for(int i = 1;i <= (2*n+5);i++) vis[i] = 0;
  queue<int> qu;  
  qu.push(s);
  d[s] = 0;
  vis[s] = 1;
  cur[s] = head[s];
  while(!qu.empty()){
    int now = qu.front();qu.pop();
    for(int i = head[now];i;i = edge[i].nex){
      int nex = edge[i].to;
      if(!vis[nex] and edge[i].cap > edge[i].flow){
	vis[nex] = 1;
	cur[nex] = head[nex];
	d[nex] = d[now] + 1;
	qu.push(nex);
	if(nex == t) return 1;
      }
    }
  }
  return 0;
}

ll dfs(int x,ll a){
  if(x == t or a == 0) return a;
  ll flow = 0,f;
  for(int i = cur[x];i and a;i = edge[i].nex){
    cur[x] = i;
    int nex = edge[i].to;
    if(d[x]+1 == d[nex] and edge[i].cap > edge[i].flow){
      f = dfs(nex,min(a,edge[i].cap-edge[i].flow));
      edge[i].flow += f;
      edge[i^1].flow -= f;
      flow += f;
      a -= f;
    }
  }
  return flow;
}

int read(){
  int x=0,f=1;char ch='[';
  while(!isdigit(ch)){ch=getchar();if(ch=='-')f=-1;}
  while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
  return x*f;
}

int main(){
  // freopen("input.txt","r",stdin);
  while(cin>>n>>m){
    for(int i = 1;i <= m;i++){
      a[i] = read()+1;b[i] = read()+1;
    }
    int ans = inf;
    for(s = 1;s <= n;s++){
      for(t = 1;t <= n;t++){
	if(s == t) continue;
	memset(head,0,sizeof(head));
	cnt = 1;
	for(int i = 1;i <= n;i++){
	  if(i == s or i == t) add_edge(i,i+n,inf-1);
	  else add_edge(i,i+n,1);
	}
	for(int i = 1;i <= m;i++){
	  add_edge(a[i]+n,b[i],inf-1);
	  add_edge(b[i]+n,a[i],inf-1);
	}
	int flow = 0;
	while(bfs()){
	  flow += dfs(s,inf);
	}
	ans = min(ans,flow);
      }
    }
    if(ans > n) ans = n;
    cout<<ans<<endl;
  }
  return 0;
}