hdu6321 对一个图进行加边或删边操作(n<=10),在每次操作后求图的匹配数为k的方案数,其中k=1,2,...n/2。
由于点的数目很少,考虑状压dp,dp[k][i]表示第k次操作后在i状态下点集中最大匹配的方案数,显然对于x个点的状态匹配数最多是x/2个。
考虑加边u-v的操作,如果对于一个状态pre,状态中没有u和v两个点,那么就可以从pre状态转移到now状态,其中now是pre以及u和v的并。删除边的时候同理。可以看到这里转移的时候只需要前一次操作的结果,于是可以使用滚动数组减小空间的使用。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<iostream>
#include<algorithm>
#define maxn 10050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int t,n,m,u,v,cnt;
char s[5];
ll dp[2][maxn],ans[maxn];
int num[maxn],sta[maxn];
int val[15];
void init()
{
cnt = 0;
val[0] = 1;
for(int i = 1;i <= 10;i++)val[i] = val[i-1] * 2;
for(int i = 0;i < 1024;i++)
{
num[i] = num[i>>1] + (i&1);
if(num[i]%2==0)
sta[cnt++] = i;
}
}
int main()
{
scanf("%d",&t);
init();
while(t--)
{
scanf("%d%d",&n,&m);
int now = 0;
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int j = 0;j < m;j++)
{
scanf("%s%d%d",s,&u,&v);
u--,v--;
int tmp = (val[u] | val[v]);
memset(ans,0,sizeof(ans));
for(int i = 0;i < cnt && sta[i] < (1<<n);i++)
{
int cur = sta[i];
dp[now ^ 1][cur] = dp[now][cur];
if((cur & tmp) == tmp)
{
if(s[0] == '+')
dp[now ^ 1][cur] = (dp[now ^ 1][cur] + dp[now][cur ^ tmp]) % mod;
else
dp[now ^ 1][cur] = (dp[now ^ 1][cur] - dp[now][cur ^ tmp] + mod) % mod;
}
ans[num[cur]/2] = (ans[num[cur]/2] + dp[now ^ 1][cur])%mod;
}
for(int i = 1;i <= n/2;i++)
printf(i == n/2 ? "%lld\n" : "%lld ",ans[i]);
now ^= 1;
}
}
return 0;
}
hdu6331 给出一个n个点和m条边的图,要求q次查询,每次查询从s到t经过不少于k条边(可重复)的最短距离。n<=50,m,k<=10000,q<=100000。
显然要先预处理点之间的距离,直接处理的复杂度是O(n^3*m),是无法接受的,考虑一个分块的dp。
dp[i][j][k]表示从i到j恰好经过k条边的最短距离
dp1[i][j][k]表示从i到j至少经过k条边的最短距离
dp2[i][j][k]表示从i到j恰好经过k*100条边的最短距离 ,k<=100
dp[i][j][k]用原图就可以处理出来,而dp1[i][j][k]=min(dp[i][t][k]+dis[t][j]),其中dis[][]是原图处理出的传递闭包。
以及dp2[i][j][k]=min(dp2[i][t][k-1]+dp[k][j][100])。
最后查询时枚举中间点e,ans=min(dp2[i][e][k/100]+dp1[e][t][k%100])。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 51
#define maxm 102
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
int kase,s,e,t,n,m,q,a,b,x;
int dis[maxn][maxn];
int maze[maxn][maxn];
int dp[maxn][maxn][maxm];
int dp1[maxn][maxn][maxm];
int dp2[maxn][maxn][maxm];
//0 k个 1 至少k 2 k*100个
int Min(int a,int b)
{
if(a < 0)return b;
else if(b < 0)return a;
return a<b?a:b;
}
void solve_dp()
{
memset(dp,-1,sizeof(dp));
memset(dp1,-1,sizeof(dp1));
memset(dp2,-1,sizeof(dp2));
for(int i = 1;i <= n;i++)
dp[i][i][0] = dp2[i][i][0] = 0;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
dis[i][j] = maze[i][j];
dis[i][i] = 0;
}
for(int k = 1;k <= n;k++)
{
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(dis[i][k] != -1 && dis[k][j] != -1)
dis[i][j]=Min(dis[i][j] , dis[i][k] + dis[k][j]);
}
}
}
for(int p = 1;p <= 100;p++)
{
for(int k = 1;k <= n;k++)
{
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(dp[i][k][p - 1] != -1 && maze[k][j] != -1)
dp[i][j][p] = Min(dp[i][j][p] , dp[i][k][p - 1] + maze[k][j]);
}
}
}
}
for(int p = 0;p <= 100;p++)
{
for(int k = 1;k <= n;k++)
{
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(dp[i][k][p] != -1 && dis[k][j] != -1)
dp1[i][j][p] = Min(dp1[i][j][p] , dp[i][k][p] + dis[k][j]);
}
}
}
}
for(int p = 1;p <= 100;p++)
{
for(int k = 1;k <= n;k++)
{
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(dp2[i][k][p - 1] != -1 && dp[k][j][100] != -1)
dp2[i][j][p] = Min(dp2[i][j][p] , dp2[i][k][p - 1] + dp[k][j][100]);
}
}
}
}
}
//0 k个 1 至少k 2 k*100个
int main()
{
scanf("%d",&kase);
while(kase--)
{
scanf("%d%d",&n,&m);
memset(maze,-1,sizeof(maze));
for(int i = 0;i < m;i++)
{
scanf("%d%d%d",&a,&b,&x);
maze[a][b] = Min(maze[a][b] , x);
}
solve_dp();
scanf("%d",&q);
for(int i = 0;i < q;i++)
{
scanf("%d%d%d",&s,&e,&t);
int t1 = t/100;
int t2 = t%100;
int ans = -1;
for(int k = 1;k <= n;k++)
{
if(dp2[s][k][t1] != -1 && dp1[k][e][t2] != -1)
ans = Min(ans , dp2[s][k][t1] + dp1[k][e][t2]);
}
printf("%d\n",ans);
}
}
return 0;
}