Boring Assignment Expressions 题解
【题意】
一些表达式,有些是直接赋值式,另外一些则是表达式,请你按照一些排列顺序放置,使得所有变量的值之和最小(哦对还有只有+号和*号)
【分析】
一句话题解:字符串处理+拓扑排序(类最短路)
真的是一眼看出怎么做。
想想10分钟,Coding10小时,(10小时都不一定够,,滑稽。
【细节】
细节是真心多啊。
看代码吧
【代码】
#include <map>
#include <set>
#include <ctime>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <string>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <functional>
using namespace std ;
#define rep(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define per(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define clr(a) memset(a, 0, sizeof(a))
#define ass(a, sum) memset(a, sum, sizeof(a))
#define lowbit(x) (x & -x)
#define mp make_pair
#define pb push_back
#define pob pop_back
#define fi first
#define se second
#define enter cout << endl
#define siz(x) ((int)x.size())
typedef unsigned long long ll ;
typedef vector <int> vi ;
typedef pair <int, int> pii ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
const int N = 100010 ;
const int INF = 0x3f3f3f3f ;
const int iinf = INT_MAX ;
const ll linf = 2e18 ;
const int MOD = 1000000007 ;
void print(int x) { cout << x << endl ; exit(0) ; }
void PRINT(string x) { cout << x << endl ; exit(0) ; }
void douout(double x){ printf("%lf\n", x + 0.0000000001) ; }
vector <string> vexp ; // 所有的表达式, 是去掉 "A = " 的
vector <string> vvar ; // 每个表达式的对应的变量
map <string, ll> vals ; // 变量对应的值
map <string, string> expr ; // 变量对应的表达式
vector <set<string> > vreq ; // 表达式中出现了哪些待定的变量
map <string, vector<int> > vidx ; // 每个变量在哪些表达式中出现
multimap <ll, int> q ; // (val,i)表示i号表达式的变量的值为val
bool bad ; ll ans ; // 总答案
vector <int> res ; // 存储哪些编号的表达式是答案
int n ;
// 处理表达式
ll eval(string s) {
vector <ll> vv(0) ;
vector <char> op(0) ;
istringstream iss(s) ;
string buf ;
while (iss >> buf) {
if (buf == "(") continue ;
else if (buf == "+") op.pb('+') ;
else if (buf == "*") op.pb('*') ;
else if (isdigit(buf[0])) vv.pb(atoi(buf.c_str())) ;
else if (isalpha(buf[0])) vv.pb(vals[buf]) ;
else if (buf == ")") {
ll x = vv.back() ; vv.pob() ;
ll y = vv.back() ;
if (op.back() == '+') {
vv.back() += x ;
if (vv.back() < x || vv.back() < y) return 0 ;
// 只有溢出才会是答案比之前都要小
}
else if (op.back() == '*') {
vv.back() *= x ;
if (vv.back() / x != y) return 0 ; // 同理
}
op.pob() ;
}
else while (1) cout << "OLE!" ;
}
return vv.back() ;
}
void init() {
vexp.clear() ; vvar.clear() ;
vals.clear() ; expr.clear() ;
vreq.assign(n, set<string>()) ;
vidx.clear() ; q.clear() ;
bad = false ; ans = 0 ;
res.clear() ;
}
signed main(){
while (cin >> n >> ws) {
init() ; // 对所有数组清零
for (int i = 0; i < n; i++) { // 分别处理每一条表达式
string s ;
getline(cin, s) ;
int p = s.find(' ') ;
vvar.pb(s.substr(0, p)) ;
vexp.pb(s.substr(p + 3)) ;
istringstream iss(vexp.back()) ;
string v ;
while (iss >> v) {
if (isalpha(v[0])) {
vals[v] = 0 ; // 初始, 表示变量
expr[v] = "" ;
vreq[i].insert(v) ; // 表达式i中出现了这个变量,重复就算了
if (siz(vidx[v]) == 0 || vidx[v].back() != i)
vidx[v].pb(i) ; // 变量->表达式,避免重复丢进
}
}
if (siz(vreq[i]) == 0) { // 表达式形如 A = 3,没有限制,丢入队列中
ll val = eval(vexp.back()) ;
q.insert(mp(val, i)) ; // 表示i号表达式的变量的值为val
}
}
while (!q.empty()) { // DAG上跑类最短路
ll val = q.begin()->first ; int i = q.begin()->second ;
q.erase(q.begin()) ;
string v = vvar[i] ;
if (vals[v] == 0) {
res.pb(i) ;
vals[v] = val ;
expr[v] = vexp[i] ; // 该变量的表达式被确定
for (int j = 0; j < siz(vidx[v]); j++) { // 给变量去更新其他表达式
int k = vidx[v][j] ;
vreq[k].erase(v) ;
if (vreq[k].empty()) { // 该表达式所有变量都已经确定
ll val = eval(vexp[k]) ;
if (val != 0) q.insert(mp(val, k)) ; // 必须满足合法(及不会溢出)
}
}
}
}
if (siz(res) < siz(vals)) bad = true ; // 判断答案合法
// 每个变量初始都有一个值(0),如果结果小于siz(vals),则说明不是解出了所有的值
for (map <string, ll>::iterator it = vals.begin(); it != vals.end(); it++) {
if (ans + it->second < ans) {
bad = true ; // 总答案溢出了
break ;
}
ans += it->second ;
}
if (bad) cout << "stupid expressions!\n" ;
else {
cout << ans << endl ;
for (int i = 0; i < siz(res); i++) { // 答案本身在存储时就是有序的
int j = res[i] ;
cout << vvar[j] << " = " << vexp[j] << endl ;
}
}
}
return 0 ;
}
/*
写代码时请注意:
1.ll?数组大小,边界?数据范围?
2.精度?
3.特判?
4.至少做一些
思考提醒:
1.最大值最小->二分?
2.可以贪心么?不行dp可以么
3.可以优化么
4.维护区间用什么数据结构?
5.统计方案是用dp?模了么?
*/